Spring Boot App on Google App Engine Standard Environment


This blog represents tips and techniques on how to deploy a Spring Boot app on Google App Engine standard environment.

Create a Google AppEngine (GAE) Project

Login into Google Cloud Console and create an AppEngine project.

Configure GCloud Setup

In order to upload GAE project from command prompt, the GCLoud setup needs to be configured. Execute following command from the command prompt to start configuring your appengine project:

gcloud init

Do some of the following while configuring the setup:

  • Select a new configuration; Enter the configuration name
  • Login into your cloud account
  • Successful login will list down existing Google Appengine (GAE) project. Pick the one which you created from the cloud console.
  • Select Yes to configure Google Compute Engine (GCE)
  • Select an appropriate GCE zone and you are all set to upload your project. The region or zone can later be changed by using command such as
    gcloud config set compute/region NAME
    gcloud config set compute/zone NAME

Create a Spring Starter Project in Eclipse IDE

This will create a boilerplate spring boot project. Select Web option as one of the dependencies while creating the spring boot project. Also, select packaging option as war.

Configure Springboot project for Appengine Standard Environment

The following steps can be used to configure Spring boot project for deployment on Appengine Standard Environment. Recall that The App Engine standard environment is based on container instances running on Google’s infrastructure. Containers are preconfigured with one of several available runtimes (Java 7, Java 8, Python 2.7, Go and PHP). Greater details can be found on page, The App Engine Standard Environment.

  • Modify POM.xml to accommodate following changes:
    • Note the exclusions element which leads to exclusion of Tomcat such that it won’t conflict with Jetty server
      <!-- Exclude Tomcat so that it doesn&amp;#39;t conflict w/ Jetty server -->

      One would also require to remove tomcat related dependencies such as following from POM.xml.

    • Use JUL-to-slf4j for redirecting logging made using JUL library (java.util.logging) to SLF4J. Often, in legacy apps, one or more components rely on a logging API such as log4j, JCL, JUL etc other than SLF4J. It is highly likely that these components may not switch to SLF4J. To deal with such circumstances, SLF4J ships with several bridging modules which redirect calls made to log4j, JCL and java.util.logging APIs to behave as if they were made to the SLF4J API instead. Spring Boot’s default logging bridge conflicts with Jetty’s logging system. To be able to capture the Spring Boot startup logs, it is required to exclude org.slf4j:jul-to-slf4j dependency.
    • Include Servlet API dependency. This is a MUST step.
    • Include Appengine Cloud Plugin
  • Add App Engine Configuration
    Create a file appengine-web.xml at the location, src/main/webapp/WEB-INF. Place following code in the file. Note that instance-class element is assigned to B1. If not assigned, the default is B2 which costs $0.10 to $0.13 per hour per instance based on the region one selects. That would turn out to be expensive to start with. Rather it would be good to get started with instance class, B1 which costs $0.05 to $0.07 per hour per instance based on the region one selects. Check out the pricing for standard app engine environment on this page, App Engine Pricing. Make sure to select most appropriate region for dev/testing purpose. Following screenshot represents regions and zones. Get further details from this page, Google Cloud Locations.

    Google Cloud Regions Zones

    Figure 1. Google Cloud Regions Zones

    <appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
  • Create index.html file
    Create an index.html file at the location, src/main/resources/static. Following is sample code for index.html with bootstrap

    <!DOCTYPE html>
    <title>Tutosphere | Welcome</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="styles.css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <!-- Latest compiled and minified JavaScript -->
    <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22https%3A%2F%2Fajax.googleapis.com%2Fajax%2Flibs%2Fjquery%2F3.2.1%2Fjquery.min.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&amp;lt;script&amp;gt;" title="&amp;lt;script&amp;gt;" />
    <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22https%3A%2F%2Fmaxcdn.bootstrapcdn.com%2Fbootstrap%2F3.3.7%2Fjs%2Fbootstrap.min.js%22%20integrity%3D%22sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa%22%20crossorigin%3D%22anonymous%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&amp;lt;script&amp;gt;" title="&amp;lt;script&amp;gt;" />
    <div class="container">
    <nav class="navbar navbar-default navbar-static-top">
    <div class="container-fluid">
    <div class="navbar-header">
    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
    <span class="sr-only">Toggle navigation</span>
    <span class="icon-bar"></span>
    <span class="icon-bar"></span>
    <span class="icon-bar"></span>
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
    <ul class="nav navbar-nav navbar-left">
    <li><a href="">Home</a></li>
    <ul class="nav navbar-nav navbar-right">
    <li><a href="">Login</a></li>
    <li><a href="">Signup</a></li>
    <div class="jumbotron">
    <h1>Welcome to Tutosphere!</h1>
    This is a platform bringing together Tutors and Students and helping them communicate, collaborate.
    <a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a>

Run Spring Boot App Locally

Execute following command to run the app locally. Make sure that app is started successfully and accessible at URL, http://localhost:8080.

mvn appengine:run

Deploy on Google Cloud

Execute following command to deploy the app on the cloud.

mvn appengine:deploy


Ajitesh Kumar

Ajitesh Kumar

Ajitesh is passionate about various different technologies including programming languages such as Java/JEE, Javascript, PHP, C/C++, mobile programming languages etc, and, computing fundamentals related with cloud-native technologies, application security, cloud computing platforms, mobile apps, big data etc.

He has also authored the book, Building Web Apps with Spring 5 and Angular.
Ajitesh Kumar

1 Comment

  1. Thank you for the article. It was so simple that I wish it would really work.

    I’m getting the following error upon mvn appengine:run (or mvn war:exploded appengine:run)

    I still can’t find solution for this. Didn’t you also getting this error, Sir?

    [INFO] ————————————————————————
    [INFO] Total time: 15.088 s
    [INFO] Finished at: 2017-10-04T20:14:43+07:00
    [INFO] Final Memory: 34M/464M
    [INFO] ————————————————————————
    [ERROR] Failed to execute goal com.google.cloud.tools:appengine-maven-plugin:1.3.1:run (default-cli) on project mtq-rekap-bot-springboot: Execution default-cli of goal com.google.cloud.tools:appengine-maven-plugin:1.3.1:run failed: Non zero exit: 1 -> [Help 1]
    [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
    [ERROR] Re-run Maven using the -X switch to enable full debug logging.
    [ERROR] For more information about the errors and possible solutions, please read the following articles:
    [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException

Leave A Reply

Time limit is exhausted. Please reload the CAPTCHA.