There are a number of different ways to deploy a Spring Boot application, each with their own advantages and disadvantages. This post aims to compare the most common deployment methods along with some guidance as to what might work best for your environment.
If after reading this article you are looking for a place to host your private Maven or Docker container repositories we encourage you to sign up for Dist which provides easy to use cloud-based repositories that are reliable, secure, and fast.
The default artifact produced by building a Spring Boot application is an executable Fat JAR. This is a safe and easy default from a deployment perspective as it only requires a JVM and the JAR file itself to be deployed. All application dependencies are bundled into the JAR file so you only need to concern yourself with how you ship the JVM and JAR file and how you manage the lifecycle of the application process. Documentation regarding this process is provided in the Spring Boot documentation for Installing Spring Boot Applications.
You can use Dist Maven repositories to host and deploy the artifacts produced by Spring Boot by following the Downloading Maven artifacts guide. This has the advantage of using a custom-built CDN that places these artifacts closer to your development and deployment locations for increased performance.
The disadvantage of using a fat JAR is that you need to ship the full JAR file which includes all dependencies on each deployment even if you have only updated a small amount of your own application code. This can result in slower and more inefficient deployments due to increased network transfer time (even when using a fast CDN) as the JAR file grows in size.
An alternative to shipping a potentially large fat JAR whilst still using the relatively simple JAR based deployment method is to use the experimental Spring Boot Thin Launcher project. Using this approach results in a “thin” JAR that just contains your application and some bootstrap code that will download your dependencies (or load from a local cache) when the application is launched. This can speed up your application deployment performance but has the potential to slow down the startup of your application. The startup performance hit can be offset by caching (and using a pre-warmed cache), however this does introduce some local state requirements and deployment complexity that may not be desirable.
If you have a requirement to deploy to an existing Java Servlet Container such as Tomcat a guide for Converting a Spring Boot JAR Application to a WAR is available that will adapt the produced fat JAR into a WAR file for deployment.
Docker containers provide a simple way to bundle your application along with all its dependencies (in this case a JVM) together. If you wish to deploy to Docker or a container orchestration system such as Kubernetes this is the deployment option that you will be using.
The easiest way to package your application into a container image is to simply add the default Fat JAR produced by Spring Boot to your container image using a simple
Dockerfile. This has the same disadvantage as fat JARs where if you are only updating a small amount of application code a potentially large build artifact is created each time due to bundling all dependencies into one image layer.
The Spring Boot Docker guide covers this simple solution along with more advanced configurations that make use of image layering to speed up your build and deployment process by splitting application code and dependencies into their own layers.
Starting from Spring Boot 2.3 out of the box support for creating Docker container images is available that automates the creation of efficiently layered images. The Spring Boot Docker guide covers this support in more detail. However, at this point in time a Docker daemon is still required to produce these images which may not be available in some build environments due to security or other concerns. Also available as a part of this release is the ability to use Paketo Buildpacks that provide a number of useful optimizations and tuning settings for your containers automatically.
The Spring Boot Docker guide also covers the usage of Jib which provides plugins for Maven and Gradle that automate the layering process whilst also operating without the need for a Docker daemon during the build process.
Using Spring Boot 2.3 Docker container image support or Jib depending on your environment are currently the best and easiest solutions for building and publishing Spring Boot application container images.
For the best deployment performance due to smaller overall image size you should consider using a base JVM image that only provide a JRE such as distroless (default with Jib), AdoptOpenJDK images, or by building your own custom JRE image with jlink.
Cloud based (PaaS)
If you are seeking a very simple deployment and operational process or your organization already uses a cloud based Platform as a services (PaaS) you will most likely want to deploy your Spring Boot applications to one of these services. The Spring Boot Deploying to the Cloud documentation covers how to run your Spring Boot application on many of the more popular platforms.
A Cloud based PaaS can offer one of the easiest deployment options as a number of deployment and operational concerns are automatically handled. However, with this simplicity comes a degree of vendor lock-in, potentially complex pricing scenarios, along with platform quirks and features that you will need to spend time investigating to ensure they are suitable for your application.
What should I use?
A Docker container based solution using a container built with Spring Boot (if using 2.3 and having access to the Docker daemon in your build environment) or Jib is currently the ideal deployment solution. The efficient layering of your application combined with a fast Docker container registry (such as Dist) results in fast builds and deployments.
If you can not deploy into a container environment or orchestration platform you should consider the following options:
- Existing deployment process that can maintain a JVM, ship JAR files and manage application lifecycle: Fat JAR.
- Existing Java Servlet Container (such as Tomcat): WAR.
- Already using a PaaS or want less deployment and operational complexity (whilst being mindful of lock-in, pricing, and quirks/features): cloud based PaaS.
There is the ongoing work by the Spring Boot team to support generating GraalVM Native Images from Spring Boot applications. These are standalone executable files that are easy to deploy that have faster startup times and lower runtime memory overheads when compared to the same application running under a JVM. This is currently an experimental project in alpha status with a number of limitations. It will be interesting to observe progress on this project as it could be interesting from a deployment point of view when it reaches production ready status.
A project to bring native image support to the standard JDK has also been recently announced titled Project Leyden. This will be an interesting project to watch as it will bring a number of the GraalVM benefits to the standard JDK.
If you still need help determining the best deployment method for your given environment or how best to integrate these deployment methods with private Dist repositories please feel free to contact us.