Spring Boot: Dockerizing a Spring Boot App
Packaging a Spring Boot application as a Docker image makes it portable and easy to deploy anywhere. Spring Boot supports multiple approaches: a simple Dockerfile, a multi-stage Dockerfile for smaller images, docker-compose for local development with dependencies, and the Buildpacks integration that builds an OCI image without a Dockerfile.
Contents
Building a JAR with Maven/Gradle
Before building a Docker image you need a runnable JAR. Spring Boot's Maven and Gradle plugins produce a fat JAR (also called an executable JAR or ΓΌber-JAR) that bundles the application classes, all dependencies, and the embedded Tomcat server into a single file. It can be run anywhere with java -jar app.jar
# Maven β produces a fat JAR in target/
./mvnw clean package -DskipTests
# Gradle β produces in build/libs/
./gradlew bootJar
Writing a Dockerfile (FROM eclipse-temurin, COPY, ENTRYPOINT)
A Dockerfile
# Simple single-stage Dockerfile
FROM eclipse-temurin:21-jre-alpine
# Non-root user for security
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
WORKDIR /app
# Copy the fat JAR built by Maven/Gradle
COPY target/*.jar app.jar
# Expose the port Spring Boot listens on
EXPOSE 8080
# JVM tuning for containers
ENV JAVA_OPTS="-XX:MaxRAMPercentage=75"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
eclipse-temurinopenjdk-jre-alpineBuilding and Running the Image
Once the Dockerfiledocker builddocker run-p-e
# Build the image β tag it with the app name and version
docker build -t my-spring-app:1.0.0 .
# Run the container
docker run -p 8080:8080 my-spring-app:1.0.0
# Pass environment variables (e.g., override Spring profile and DB URL)
docker run -p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/mydb \
my-spring-app:1.0.0
# Run in background
docker run -d --name my-app -p 8080:8080 my-spring-app:1.0.0
# View logs
docker logs -f my-app
Multi-Stage Build for Smaller Images
A multi-stage build compiles and packages the JAR in one stage, then copies only the result into a minimal runtime image, removing the JDK, Maven, source code, and build artifacts from the final image.
# Stage 1 β build
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /build
COPY .mvn/ .mvn/
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline -q
COPY src/ src/
RUN ./mvnw clean package -DskipTests -q
# Stage 2 β runtime (JRE only β no compiler, no Maven)
FROM eclipse-temurin:21-jre-alpine
RUN addgroup -S app && adduser -S app -G app
USER app
WORKDIR /app
# Use Spring Boot's layered JAR extraction for better layer caching
COPY --from=builder /build/target/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
EXPOSE 8080
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75",\
"org.springframework.boot.loader.launch.JarLauncher"]
docker-compose with App + Database
Running the app container in isolation is fine for smoke-testing, but real applications depend on a database, cache, or message broker. docker composedocker-compose.ymldepends_onhealthcheck
# docker-compose.yml
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: mydb
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myuser -d mydb"]
interval: 10s
timeout: 5s
retries: 5
app:
build: .
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/mydb
SPRING_DATASOURCE_USERNAME: myuser
SPRING_DATASOURCE_PASSWORD: mypassword
SPRING_JPA_HIBERNATE_DDL_AUTO: update
SPRING_PROFILES_ACTIVE: prod
depends_on:
db:
condition: service_healthy
volumes:
postgres_data:
The most common docker compose
# Start all services
docker compose up --build
# Start in background
docker compose up -d --build
# Stop all services
docker compose down
# Stop and remove volumes (wipes database)
docker compose down -v
Spring Boot Buildpacks (./mvnw spring-boot:build-image)
Spring Boot 2.3+ integrates with Cloud Native Buildpacks to build a Docker image directly from your source code or JAR: no Dockerfile required. The resulting image follows best practices automatically (layering, non-root user, etc.).
# Build an OCI image using Buildpacks (Docker must be running)
./mvnw spring-boot:build-image -DskipTests
# Run the resulting image
docker run -p 8080:8080 my-spring-app:0.0.1-SNAPSHOT
<!-- Customise image name and JVM settings in pom.xml -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>myregistry/my-spring-app:${project.version}</name>
<env>
<BP_JVM_VERSION>21</BP_JVM_VERSION>
</env>
</image>
</configuration>
</plugin>