← All articles
Backend1 min read

Deploying Spring Boot with Docker

Layered images, multi-stage builds, and JVM tuning for containers. Ship small, fast-starting Spring Boot images to production.

S

Swapnika Voora

Author

Containers are the standard unit of deployment for Spring Boot services. A naive image works, but a little care produces smaller images that build faster and start quicker.

A multi-stage build

Build the JAR in one stage and copy only the artifact into a slim runtime image. This keeps your Maven cache and toolchain out of the final layer.

Dockerfile
# Build stage
FROM eclipse-temurin:21-jdk AS build
WORKDIR /app
COPY . .
RUN ./mvnw -q package -DskipTests
 
# Runtime stage
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /app/target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Layered JARs for better caching

Spring Boot can split the JAR into layers so dependencies (which rarely change) cache separately from your code (which changes constantly).

Dockerfile.layered
FROM eclipse-temurin:21-jre
WORKDIR /app
ARG JAR=target/app.jar
COPY --from=build /app/dependencies/ ./
COPY --from=build /app/spring-boot-loader/ ./
COPY --from=build /app/snapshot-dependencies/ ./
COPY --from=build /app/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]

Let the JVM see the container

Modern JVMs are container-aware, but you can tune how much of the container's memory the heap is allowed to use.

jvm.dockerfile
ENV JAVA_OPTS="-XX:MaxRAMPercentage=75.0"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

Checklist for production images

  • Use a JRE base image, not a full JDK, at runtime.
  • Enable layered JARs so rebuilds only ship changed layers.
  • Set MaxRAMPercentage instead of a fixed -Xmx.
  • Add a health check hitting /actuator/health.

These steps typically cut image size and cold-start time noticeably while keeping the Dockerfile easy to read.

#java#spring-boot#docker#devops

More in Backend