Building Java Spring Boot Docker containers often leads to bloated images (800MB+) if the JDK build tools remain in the final runtime. Additionally, Spring Boot needs properly mapped datasource environment variables and healthcheck endpoints to ensure zero-downtime restarts.
This production setup separates the Maven packaging phase from the lightweight OpenJDK JRE runner, connects to a tuned PostgreSQL 15 volume, and proxies through Caddy for automatic HTTPS.
Configures resource constraints and passes Spring datasource environment parameters directly to the application container.
version: '3.8'
services:
caddy:
image: caddy:2-alpine
container_name: spring_proxy
restart: always
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
depends_on:
backend:
condition: service_healthy
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: spring_app
restart: always
expose:
- "8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- SPRING_DATASOURCE_URL=jdbc:postgresql://database:5432/${DB_NAME:-spring_db}
- SPRING_DATASOURCE_USERNAME=${DB_USER:-spring_user}
- SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD:-spring_password}
deploy:
resources:
limits:
cpus: '1.0'
memory: 1024M
depends_on:
database:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 15s
timeout: 5s
retries: 3
database:
image: postgres:15-alpine
container_name: spring_postgres
restart: always
environment:
- POSTGRES_USER=${DB_USER:-spring_user}
- POSTGRES_PASSWORD=${DB_PASSWORD:-spring_password}
- POSTGRES_DB=${DB_NAME:-spring_db}
volumes:
- pg_data:/var/lib/postgresql/data
deploy:
resources:
limits:
cpus: '1.0'
memory: 1024M
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-spring_user} -d ${DB_NAME:-spring_db}"]
interval: 10s
timeout: 5s
retries: 5
volumes:
caddy_data:
caddy_config:
pg_data:
Caches `pom.xml` dependencies separately to avoid re-downloading Maven packages on source code edits, leaving a slim ~180MB runtime container.
# --- Stage 1: Build Application JAR ---
FROM maven:3.8-openjdk-17-slim AS builder
WORKDIR /app
COPY pom.xml ./
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests
# --- Stage 2: Production JRE Runtime ---
FROM openjdk:17-jdk-slim
WORKDIR /app
# Security: Run as dedicated non-root user
RUN groupadd -g 1000 spring && useradd -u 1000 -g spring spring
COPY --from=builder /app/target/*.jar app.jar
RUN chown -R spring:spring /app
USER spring
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]