Docker containerization and deployment for Java/Spring Boot applications. Multi-stage builds, docker-compose, health checks, and CI/CD with GitHub Actions.
Use when:
Do NOT use when:
Always use multi-stage builds to separate the build environment from the runtime. This produces smaller, more secure images.
# Stage 1: Build
FROM eclipse-temurin:21-jdk AS builder
WORKDIR /app
COPY pom.xml .
COPY .mvn/ .mvn/
COPY mvnw .
RUN chmod +x mvnw && ./mvnw dependency:go-offline -B
COPY src/ src/
RUN ./mvnw package -DskipTests -B
# Stage 2: Runtime
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=builder /app/target/*.jar app.jar
RUN chown -R appuser:appgroup /app
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-jar", "app.jar"]
.dockerignore to exclude unnecessary files from the build contextModern JVMs are container-aware (Java 10+), but explicit tuning is still recommended.
# Memory: use percentage-based limits so the JVM respects container memory limits
-XX:MaxRAMPercentage=75.0
-XX:InitialRAMPercentage=50.0
-XX:MinRAMPercentage=25.0
# GC selection
-XX:+UseG1GC # General-purpose, good for most workloads
-XX:+UseZGC # Low-latency, good for large heaps (Java 17+)
-XX:+UseShenandoahGC # Alternative low-latency GC
# Container awareness (enabled by default in Java 10+, but explicit is safer)
-XX:+UseContainerSupport
# Diagnostics
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp/heapdump.hprof
-XX:+ExitOnOutOfMemoryError
| Container Memory Limit | MaxRAMPercentage | Reasoning |
|---|---|---|
| 256 MB | 75% | Leaves ~64 MB for OS, native memory, threads |
| 512 MB | 75% | Leaves ~128 MB for overhead |
| 1 GB | 75% | Standard for most microservices |
| 2 GB+ | 80% | Larger containers can use a higher percentage |
If your container is killed by the OOM killer (exit code 137), the JVM heap is exceeding the container memory limit. Solutions:
MaxRAMPercentage (e.g., from 75% to 60%)-XX:NativeMemoryTracking=summary for diagnosticsUse Docker Compose to run your application alongside its dependencies.
project/
docker-compose.yml # Base configuration (production-like)
docker-compose.dev.yml # Dev overrides (debug, live reload)
docker-compose.test.yml # Test overrides (test DB, mocks)
.env # Environment variables (not committed)
.env.example # Template for environment variables (committed)
# Development
docker compose -f docker-compose.yml -f docker-compose.dev.yml up
# Testing
docker compose -f docker-compose.yml -f docker-compose.test.yml up
# Production (base only)
docker compose up -d
Always use depends_on with condition: service_healthy so your application starts only after its dependencies are ready: