Deploying Spring Boot Applications to Heroku
There are tons of ways to deploy Spring Boot applications to Heroku. You can deploy using the Heroku CLI, deploy a JAR file without uploading the source code, and more.
Join the DZone community and get the full member experience.
Join For FreeThis example demonstrates the deployment options of Spring Boot applications to Heroku. The topics covered are the following:
- Deploy using the Heroku CLI.
- Deploy using the Heroku Maven plugin.
- Deploy a JAR file without uploading the source code.
- Spring boot configuration for Heroku.
- Deploy the multi-module Maven project.
- Implement communication among applications.
- Use web/worker processes.
- Deploy using Travis CI.
Technology Used
- Spring Boot 1.4. release.
- Heroku.
- Maven 3.
Deploying Spring Boot to Heroku Using Git
Deploying Spring Boot to Heroku is quite easy because Heroku supports Java applications packaged as WAR and JAR out of the box. The prerequisites are
- Create a Heroku account.
- Install the Heroku CLI.
- Set up Heroku locally using your Heroku login.
Once this is done, the steps are straightforward.
#Download an empty Spring Boot application
curl https://start.spring.io/starter.tgz -d style=web -d style=actuator -d name=heroku-example | tar -xzvf -
git init
git add .
git commit -m "initial commit"
#Creates a Heroku application with random name (and domain)
#Creates a Hit repository at heroku# registers the remote repository by the name Heroku
heroku create
#Pushes the source code to the Heroku Git repo, so Heroku can build and deploy it
git push heroku master
#opens our app in a browser
heroku open
What happens under the hood is:
- After pushing code to the remote repository named Heroku, the build process is triggered.
- Given that the project has a pom.xml, Heroku recognizes it as a Java application.
- Spring Boot has an embedded Tomcat. Therefore, it can be started up as a JAR file and will work as a web server.
This process is well-described in the official Heroku Dev Center article. This is really easy and everything works out of the box for simple applications, but it raises some concerns:
- What if my project is not using Maven?
- What if I am not using Git?
- What if I don't want Heroku to build my app from source code because:
- I am working a closed-source project and don't want to send the code to Heroku?
- I am using maven dependencies available only in my company's internal maven repo, so the code can't be compiled on Heroku?
- What if my project is a multi-module?
- What if I want to deploy something to Heroku without making a Git commit?
We will address these concerns one by one.
Deploying JAR File Without Source Code
The Heroku deploy CLI plugin can directly send JAR files to Heroku. Its usage is quite easy.
heroku plugins:install
heroku-cli-deploymvn
clean install
# Creates an app with the specified name, without setting up a git remote
heroku create --no-remote
#deploys the jar file
heroku deploy:jar target/demo-0.0.1-SNAPSHOT.jar --app
This is well-described in the official Dev Center article until this point. However, interestingly, we open the app using
heroku open --app
Nothing happens. The application is not accessible. Looking into the logs using this:
heroku logs --app
will explain the reasons. There is a relevant log line saying:
Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch Stopping process with SIGKILL
Heroku expects our application to listen on the port specified by the PORT environment variable, but it doesn't. There are a few ways to fix this.
1. Properties
Add the following to application.properties:
server.port=${PORT:8080}
Then, rebuild and redeploy the app. This time, the port will be read from the PORT environment variable. Locally where the PORT environment variable is not set it will fall back to 8080. The same can be achieved, by overriding the server.port in a different spring profile
2. Procfile
Heroku supports AWS-style Procfile. Create one in the application root directory (with capital P) and enter:
java $JAVA_OPTS -jar target/demo-0.0.1-SNAPSHOT.jar --server.port=$PORT $JAR_OPTS
Then, deploy the app using:
heroku deploy:jar -j target/demo-0.0.1-SNAPSHOT.jar -i Procfile --app
3. Passing Options
The heroku deploy:jar
command can pass additional parameters to the jar file, so it should be possible to execute
heroku deploy:jar target/demo-0.0.1-SNAPSHOT.jar -o --server.port=$PORT --app
However, for some reason, the escaping seems to be broken and it does not work.
Deploying JAR File Using Heroku Maven Plugin
Using the Heroku Maven plugin has a number of advantages:
- It maintains all configuration in a single place, eliminating the need of a Procfile.
- It requires no installation of heroku CLI and heroku deploy CLI plugin.
- It works out of the box on any CI server without any additional setup.
Configure the plugin like this:
<properties>
<full-artifact-name>target/${project.artifactId}-${project.version}.jar</full-artifact-name>
</properties>
<build>
<plugins>
<plugin>
<groupId>com.heroku.sdk</groupId>
<artifactId>heroku-maven-plugin</artifactId>
<version>1.1.1</version>
<configuration>
<appName>YOUR APP NAME COMES HERE</appName>
<includeTarget>false</includeTarget>
<includes>
<include>${basedir}/${full-artifact-name}</include>
</includes>
<jdkVersion>1.8</jdkVersion>
<processTypes>
<web>java $JAVA_OPTS -jar ${full-artifact-name}</web>
</processTypes>
</configuration>
</plugin>
</plugins>
</build>
Once the plugin is set up, the application can be deployed with the following commands:
#if the application is packaged as jar
mvn heroku:deploy
#if the application is packaged as war
mvn heroku:deploy-war
Heroku CLI has to be installed because the Maven plugin will read authentication information from there. To eliminate installation, the Maven plugin can be executed like this:
HEROKU_API_KEY="YOUR API KEY COMES HERE" mvn heroku:deploy
The API key can be found here. The official description of the heroku maven plugin is here.
Deploying Multiple Applications That Communicate With Each Other
If there are multiple applications communicating with each other then heroku supports two approaches.
1. Create a Single Application With a Web and a Worker Process
Within a single application, multiple processes can be created. The free tier allows two process types, but only if credit card details are provided. It can be either defined in a proc file:
web: java $JAVA_OPTS -jar web-service/target/web-service-0.0.1-SNAPSHOT.jar --server.port=$PORT
worker: java $JAVA_OPTS -jar worker-service/target/worker-service-0.0.1-SNAPSHOT.jar
...or in the Maven plugin:
<includes>
<include>${full-artifact-name}</include>
<include>${worker-full-artifact-name}</include>
</includes>
<processTypes>
<web>java $JAVA_OPTS -jar ${full-artifact-name}</web>
<worker>java $JAVA_OPTS -jar ${worker-full-artifact-name}</worker>
</processTypes>
The benefit of this approach is that multiple processes can be deployed in a single step. Still, they can be scaled independently using:
heroku ps:scale web=2 worker=4
There are also some limitations:
- Only the web process can be accessed over HTTP. This is true not just to end users, but even the web process won't be able to access the worker process through internal network. It can be addressed even by subscribing to the enterprise package that has private spaces or by connecting the processes using messaging, such as RabbitMQ, which is available in the free tier, too.
- The processes can't be deployed independently. The deployment of an application must always contain all processes; otherwise, the missing processes are removed.
Due to these two limitations, implementing a microservice project with lots of services is not really efficient this way.
2. Create Multiple Applications
A more robust option is to create separate Heroku applications for every application. If applications are separate Maven applications then nothing additional is required, just configure the Heroku plugin with the correct application name. All applications will be web workers available on the public internet. Heroku applications still won't be able to communicate with each other in a private network. Therefore, they need to know each other's public address.
This is not just a possible source of performance degradation, but also may cause a security risk as every application must make sure that correct authentication and authorization is implemented. If applications are in a multi module project, then all modules should include the Heroku plugin (with different application names configured). If it is required to deploy all of them them with a single command, then the Heroku plugin can be moved to a profile like this:
<profiles>
<profile>
<id>heroku</id>
<activation>
<property>
<name>heroku</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>com.heroku.sdk</groupId>
<artifactId>heroku-maven-plugin</artifactId>
<version>1.1.1</version>
<configuration>
<appName>YOUR APP NAME COMES HERE</appName>
<includeTarget>false</includeTarget>
<includes>
<include>${full-artifact-name}</include>
</includes>
<jdkVersion>1.8</jdkVersion>
<processTypes>
<web>java $JAVA_OPTS -jar ${full-artifact-name} --spring.profiles.active=heroku</web>
</processTypes>
</configuration>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>deploy</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
...and triggered from the project root by like this:
mvn clean install -Pheroku
Creating a CI/CD Pipeline and Deploying With Travis CI
Using the the above knowledge, the CI/CD process can be set up. This example uses Travis and GitHub, but these steps can be implemented by combining almost any other VCS and CI tool. We aim for the following workflow:
- Developer commits to Git.
- Travis CI builds the project.
- Travis CI executes the Heroku Maven plugin to deploy the applications to Heroku.
Steps are:
- Link your GitHub account with Travis CI. Steps are here.
- Enable build triggering for your repo.
- Store the Heroku API key in a repository variable named
HEROKU_API_KEY
as described here. - Create a .travis.yml file with the below content
language: java
jdk: oraclejdk8
script: HEROKU_API_KEY="$HEROKU_API_KEY" mvn clean verify -Pheroku
Once the build is triggered using the API key and the Maven Heroku plugin, application(s) will be deployed to Heroku. Environment variable values are not displayed in the build logs; therefore, it is a safe option.
Summary
Using Spring Boot, Heroku, GitHub, and Travis CI, a complete application can be created and deployed using free tools (or at least the free versions of paid tools). Heroku itself is very well documented and easy to use. The free version even allows custom domain names. Therefore, it is suitable for professional use, as well. Some advanced settings like private networks are not available in the free subscription.
Bonus: Heroku Pipelines
The build pipeline should automatically deploy commits to the dev environment. The staging or prod deployment should be a manual process after QA approval. The Heroku pipeline is a handy tool to move specific application versions across environments. The full description is here.
Source code and references
A source code with fully integrated CI/CD can be found here.
Published at DZone with permission of Peter Szanto. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Seven Steps To Deploy Kedro Pipelines on Amazon EMR
-
Alpha Testing Tutorial: A Comprehensive Guide With Best Practices
-
Design Patterns for Microservices: Ambassador, Anti-Corruption Layer, and Backends for Frontends
-
Micro Frontends on Monorepo With Remote State Management
Comments