Containerisation with Docker
IntermediateEach microservice is packaged as a Docker image; multi-stage builds keep images small, and images are tagged with the commit SHA for traceability.
Overview
Containerisation with Docker packages a microservice and all its dependencies (JRE, config, static assets) into a single portable image that runs identically in dev, staging, and production. Each microservice produces its own Docker image with a semantic version or commit SHA tag. The image is stored in a container registry (ECR, GCR, Docker Hub) and pulled by Kubernetes when deploying. For Spring Boot, the key best practices are: multi-stage builds to keep images small, layered JARs to maximise Docker layer cache reuse (fast rebuilds), running as a non-root user, and using a minimal base image. Spring Boot 2.3+ can also produce OCI images directly with the Buildpacks plugin — no Dockerfile needed.
Dockerfile for Spring Boot — Multi-Stage Build
A multi-stage Dockerfile separates the build environment from the runtime environment. The final image contains only the JRE and the application — no Maven/Gradle, no source code. This keeps the image small and eliminates build tools from the attack surface.
# Stage 1: Build — uses full JDK + Maven, not included in final image
FROM eclipse-temurin:21-jdk AS builder
WORKDIR /build
# Cache dependency layer separately (only invalidated when pom.xml changes)
COPY pom.xml .
COPY .mvn .mvn
COPY mvnw .
RUN ./mvnw dependency:go-offline -q
# Copy source and build
COPY src ./src
RUN ./mvnw package -DskipTests -q
# Extract layered JAR (Spring Boot 2.3+ layered JAR support)
RUN java -Djarmode=layertools -jar target/*.jar extract --destination extracted
# Stage 2: Runtime — minimal JRE image, no build tools
FROM eclipse-temurin:21-jre AS runtime
WORKDIR /app
RUN addgroup --system app && adduser --system --group app
USER app # run as non-root
# Copy layers in order of change frequency (stable layers first for cache)
COPY --from=builder /build/extracted/dependencies/ ./
COPY --from=builder /build/extracted/spring-boot-loader/ ./
COPY --from=builder /build/extracted/snapshot-dependencies/ ./
COPY --from=builder /build/extracted/application/ ./
EXPOSE 8080
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]Image Tagging and Registry
Images should be tagged with immutable identifiers for traceability and rollback:
- **Commit SHA tag**: `myregistry/order-service:a3f8c1d` — points to exact code, never changes. - **Semantic version**: `myregistry/order-service:1.4.2` — human-readable, used alongside SHA. - **Avoid `latest` in production** — mutable tag; different builds can produce the same `latest`, making rollback impossible.
Kubernetes Deployment manifests should always pin to a specific tag; CI/CD pipelines update the tag on each deployment.
# Build and tag with commit SHA + semantic version
export VERSION=1.4.2
export COMMIT_SHA=$(git rev-parse --short HEAD)
export IMAGE=123456789.dkr.ecr.us-east-1.amazonaws.com/order-service
docker build -t ${IMAGE}:${VERSION} -t ${IMAGE}:${COMMIT_SHA} .
docker push ${IMAGE}:${VERSION}
docker push ${IMAGE}:${COMMIT_SHA}
# Kubernetes Deployment — always pin to specific tag
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
template:
spec:
containers:
- name: order-service
image: 123456789.dkr.ecr.us-east-1.amazonaws.com/order-service:1.4.2
imagePullPolicy: IfNotPresent # don't re-pull if already on node
# Spring Boot Buildpacks (no Dockerfile needed)
# Produces an OCI image via Cloud Native Buildpacks
./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=${IMAGE}:${VERSION}Container Best Practices for Production
Key production hardening practices for Spring Boot containers:
1. **Non-root user** — never run as root; reduces blast radius if container is compromised. 2. **Read-only filesystem** — mount /tmp as tmpfs; prevents writes to container FS. 3. **Resource limits** — always set CPU/memory requests and limits in K8s; prevents noisy-neighbour starvation. 4. **Health probe endpoints** — /actuator/health/liveness and /readiness. 5. **Graceful shutdown** — server.shutdown=graceful to drain in-flight requests. 6. **JVM memory tuning** — set -XX:MaxRAMPercentage=75.0 to size heap relative to container memory limit.
# Kubernetes Deployment — production hardening
spec:
containers:
- name: order-service
image: myregistry/order-service:1.4.2
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
env:
- name: JAVA_TOOL_OPTIONS
value: "-XX:MaxRAMPercentage=75.0 -XX:+UseContainerSupport -Xss512k"
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
volumeMounts:
- name: tmp-dir
mountPath: /tmp
volumes:
- name: tmp-dir
emptyDir: {}Key Points to Remember
- 1Multi-stage Dockerfile: build in JDK stage, copy only the JAR to a minimal JRE runtime stage — keeps images small and secure.
- 2Layered JARs (Spring Boot 2.3+) split the fat JAR into layers ordered by change frequency, maximising Docker layer cache reuse.
- 3Tag images with commit SHA and semantic version; never use `latest` in production — it prevents reliable rollbacks.
- 4Always run containers as non-root users — set runAsNonRoot: true and a specific UID in the K8s securityContext.
- 5Set JVM flag -XX:MaxRAMPercentage=75.0 so the JVM respects container memory limits instead of using the host RAM.
- 6Always set K8s resource requests and limits — requests are used for scheduling; limits cap CPU/memory consumption.
Interview Questions
Sign in to ask AriaWhat is a multi-stage Docker build and why is it useful for Spring Boot services?
Why should you avoid using the `latest` tag in a Kubernetes Deployment?
What is a layered JAR in Spring Boot and how does it improve Docker build performance?
What JVM flag should you set to make the JVM respect container memory limits?
What security settings would you apply to a Spring Boot container running in Kubernetes?
Ask Aria about Containerisation with Docker
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.