
How to Dockerize and Deploy Spring Boot
Lukas MauserThis Dockerfile compiles your Spring Boot app into a runnable jar with the Maven wrapper, then runs it on a small JRE image, using a multi-stage build so the JDK and build cache never end up in the final image.
If you're just here to copy and paste, here's the final Dockerfile that will produce an image for your Spring Boot app:
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
# Copy the Maven wrapper and pom first so dependencies are cached
COPY .mvn/ .mvn/
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline -B
# Copy the source and build the jar
COPY src ./src
RUN ./mvnw clean package -DskipTests
FROM eclipse-temurin:21-jre-alpine AS runtime
WORKDIR /app
# Copy just the built jar from the build stage
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
And here's the .dockerignore file:
target
.git
.idea
*.iml
To build and run the image, use these commands:
docker build -t spring-app .
docker run -p 8080:8080 spring-app
Not just here to copy and paste? Let's go over what's happening in the Dockerfile!
The Setup
For this tutorial, I assume you have a Spring Boot project that uses Maven and the Maven wrapper (the mvnw script and .mvn folder that Spring Initializr generates). If you use Gradle, the idea is identical, you just swap the wrapper and build commands for ./gradlew bootJar.
A Spring Boot jar is self-contained. It bundles an embedded web server, so once it is built you only need a Java runtime to start it. That is why the runtime stage can be so small.
The Dockerfile
The build stage compiles the app:
- Base image:
- Uses
eclipse-temurin:21-jdk-alpine, a full JDK on Alpine, because compiling needs the compiler.
- Dependency caching:
- Copies the Maven wrapper and
pom.xmlfirst, then runsdependency:go-offline. This downloads all dependencies into a layer that Docker caches, so a code change doesn't trigger a full re-download every build.
- Build step:
- Copies the source and runs
mvnw clean package -DskipTests. That produces a runnable jar in thetargetfolder. Tests are skipped here on purpose, since you'll normally run them in CI before building the image.
The runtime stage stays lean:
- Runtime image:
- Uses
eclipse-temurin:21-jre-alpine, which ships only the Java runtime, not the compiler and tooling. - Copies just the jar from the build stage with a wildcard, so you don't have to hardcode the version in the filename.
- Exposes port 8080, Spring Boot's default, and starts the app with
java -jar.
Configuration like your database URL should come from environment variables. Spring Boot maps them onto properties automatically, so SPRING_DATASOURCE_URL sets spring.datasource.url without any extra work.
Deployment
You can deploy this Docker container to any cloud provider that supports Docker. For example, you could use platforms like Heroku, DigitalOcean, or AWS ECS. Because I am the co-founder of Sliplane, I will show you how to deploy it there.
After signing up, you can create a new service by selecting your GitHub repository. Then keep the default settings, add any environment variables your app needs, and click deploy.

After deployment your Spring Boot app will be available under a subdomain of sliplane.app, usually it's just your service name.
You can also see the logs of your app, view metrics such as CPU and memory usage, add persistent storage, and much more. Whenever you push to your repository, Sliplane will automatically deploy your app.
If you want to try out Sliplane, the first 2 days are free! Try it out and let me know what you think :)
Next Steps
For larger apps you can shrink rebuild times further with Spring Boot's layered jars, which split dependencies and your own classes into separate Docker layers. Want help setting that up or connecting a managed database? Feel free to reach out!
Cheers,
Lukas