Spring Boot with Docker & Deployment
IntermediateSpring Boot Maven/Gradle plugins build OCI-compliant images via Buildpacks or a Dockerfile; layered JARs reduce rebuild time by keeping dependencies in separate layers.
Overview
Containerising a Spring Boot application can be done three ways: a traditional Dockerfile, the Spring Boot Maven/Gradle plugin with Buildpacks (no Dockerfile needed), or layered JARs with a hand-crafted Dockerfile. Buildpacks (./mvnw spring-boot:build-image) produce optimised, security-hardened images with automatic layer caching and are the recommended approach for teams that want opinionated defaults. Layered JARs split the application archive into dependency, snapshot-dependency, resources, and application layers, so Docker only rebuilds the application layer on code changes — drastically speeding up incremental builds. Understanding the correct JVM flags for containers (MaxRAMPercentage, heap sizing) is essential to avoid OOMKills in Kubernetes.
Building images with Spring Boot Buildpacks
The Spring Boot plugin integrates with Paketo Buildpacks to create an OCI image without a Dockerfile. The plugin automatically selects the JDK, configures memory, adds security hardening, and creates efficient layers. Images are built with ./mvnw spring-boot:build-image and pushed to a registry. The image name can be customised in pom.xml.
<!-- pom.xml — configure Buildpack image name and publish -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>registry.example.com/myapp:${project.version}</name>
<publish>true</publish>
<env>
<!-- JVM memory tuning in container -->
<BPL_JVM_THREAD_COUNT>50</BPL_JVM_THREAD_COUNT>
<BPL_JVM_HEAP_PERCENT>75</BPL_JVM_HEAP_PERCENT>
</env>
</image>
<docker>
<publishRegistry>
<url>registry.example.com</url>
<username>${REGISTRY_USER}</username>
<password>${REGISTRY_PASSWORD}</password>
</publishRegistry>
</docker>
</configuration>
</plugin>
# Build and push
./mvnw spring-boot:build-image -DskipTests
# Run locally
docker run -p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=dev \
registry.example.com/myapp:1.0.0Layered JARs with multi-stage Dockerfile
Spring Boot 2.3+ supports layered JARs: the archive is split into dependencies (rarely changed), snapshot-dependencies, spring-boot-loader, and application layers. A multi-stage Dockerfile extracts these layers, allowing Docker to cache the dependency layer across builds — only the application layer changes per commit.
# Step 1: enable layered JAR in pom.xml (default in Boot 2.3+)
# <layers><enabled>true</enabled></layers> in spring-boot-maven-plugin
# Inspect layers
java -Djarmode=layertools -jar target/myapp.jar list
# dependencies
# spring-boot-loader
# snapshot-dependencies
# application
# Multi-stage Dockerfile
FROM eclipse-temurin:21-jre AS builder
WORKDIR /app
COPY target/myapp.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
FROM eclipse-temurin:21-jre
WORKDIR /app
# Copy layers in order from least- to most-changed
# Docker caches unchanged layers between builds
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./
# Non-root user for security
RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser
USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]JVM container tuning and Kubernetes deployment
Without container-aware JVM flags, the JVM sees the host's total RAM and may size the heap too large, causing OOMKills. Use -XX:MaxRAMPercentage to set heap as a fraction of container memory. Combine with CPU flag -XX:ActiveProcessorCount to prevent thread-count over-provisioning. In Kubernetes, always set both requests and limits for predictable scheduling.
# Kubernetes Deployment with proper resource limits and JVM tuning
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
template:
spec:
containers:
- name: order-service
image: registry.example.com/order-service:1.0.0
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: prod
- name: JAVA_OPTS
value: >-
-XX:MaxRAMPercentage=75.0
-XX:InitialRAMPercentage=50.0
-XX:+UseG1GC
-XX:+UseContainerSupport
-Djava.security.egd=file:/dev/./urandom
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "512Mi" # same as request to prevent burstable class
cpu: "1000m"
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 20
periodSeconds: 10
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 15Key Points to Remember
- 1Buildpacks (spring-boot:build-image) create optimised OCI images without a Dockerfile — recommended for standard deployments
- 2Layered JARs (default in Boot 2.3+) split the archive into dependency/application layers, enabling Docker layer caching
- 3Multi-stage Dockerfiles extract layers in order from least-changed to most-changed to maximise build cache reuse
- 4-XX:MaxRAMPercentage and -XX:+UseContainerSupport ensure the JVM uses container memory limits, not host RAM
- 5Non-root user in Dockerfile reduces attack surface — never run JVM containers as root
- 6Set Kubernetes memory requests == limits to prevent the pod being placed in the Burstable QoS class and risk OOMKill
Interview Questions
Sign in to ask AriaWhat are the three approaches to containerising a Spring Boot application and what are the trade-offs?
What is a layered JAR and how does it reduce Docker build time in CI/CD pipelines?
Without -XX:MaxRAMPercentage, what happens when a Spring Boot container runs in a memory-limited pod?
Why should you set Kubernetes memory requests equal to limits for a Java service?
How would you configure liveness and readiness probes for a Spring Boot application in Kubernetes?
Ask Aria about Spring Boot with Docker & Deployment
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.