How to Dockerize a Spring Boot Application: A Complete Guide
Docker has become an essential tool for modern application deployment, offering consistency across different environments and simplifying the deployment process. In this comprehensive guide, we'll walk through dockerizing a Spring Boot application step by step.
Why Dockerize Your Spring Boot Application?
Before diving into the implementation, let's understand the benefits:
- Consistency: Your application runs the same way in development, testing, and production
- Isolation: Dependencies are contained within the Docker image
- Scalability: Easy to scale horizontally with container orchestration
- Portability: Deploy anywhere Docker is supported
- Version Control: Docker images can be versioned and rolled back
Prerequisites
Before starting, make sure you have:
- Java 17 or later installed
- Maven or Gradle
- Docker installed and running
- Basic knowledge of Spring Boot
Step 1: Create a Simple Spring Boot Application
First, let's create a basic Spring Boot application. If you already have one, skip to Step 2.
Using Spring Initializr
- Go to start.spring.io
- Choose:
- Project: Maven
- Language: Java
- Spring Boot: 3.2.0 (or latest stable)
- Dependencies: Spring Web
Sample REST Controller
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/")
public String hello() {
return "Hello from Dockerized Spring Boot!";
}
@GetMapping("/health")
public String health() {
return "Application is running!";
}
}
Application Properties
# src/main/resources/application.properties
server.port=8080
spring.application.name=demo-app
Step 2: Create a Dockerfile
Create a file named Dockerfile in your project root:
Basic Dockerfile
FROM eclipse-temurin:17-jdk-alpine
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Multi-Stage Dockerfile (Recommended)
This approach builds the application inside Docker, resulting in a smaller final image:
# Stage 1: Build the application
FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /app
# Copy pom.xml and download dependencies
COPY pom.xml .
RUN mvn dependency:go-offline
# Copy source code and build
COPY src ./src
RUN mvn clean package -DskipTests
# Stage 2: Create the runtime image
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# Copy the jar from build stage
COPY --from=build /app/target/*.jar app.jar
# Create a non-root user
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Step 3: Create .dockerignore
Create a .dockerignore file to exclude unnecessary files:
target/
!target/*.jar
.git
.gitignore
README.md
*.md
.mvn/
mvnw
mvnw.cmd
Step 4: Build Your Docker Image
Build the Application (if using basic Dockerfile)
# Build with Maven
mvn clean package -DskipTests
# Build with Gradle
./gradlew build
Build the Docker Image
docker build -t spring-boot-app:latest .
For multi-platform builds:
docker buildx build --platform linux/amd64,linux/arm64 -t spring-boot-app:latest .
Step 5: Run Your Docker Container
Basic Run
docker run -p 8080:8080 spring-boot-app:latest
Run with Environment Variables
docker run -p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e SERVER_PORT=8080 \
spring-boot-app:latest
Run in Detached Mode
docker run -d \
--name my-spring-app \
-p 8080:8080 \
--restart unless-stopped \
spring-boot-app:latest
Step 6: Docker Compose (Optional but Recommended)
Create a docker-compose.yml file for easier management:
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: spring-boot-app
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- JAVA_OPTS=-Xmx512m -Xms256m
restart: unless-stopped
networks:
- spring-network
networks:
spring-network:
driver: bridge
With Database (MySQL Example)
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: spring-boot-app
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/mydb
- SPRING_DATASOURCE_USERNAME=root
- SPRING_DATASOURCE_PASSWORD=rootpassword
depends_on:
mysql:
condition: service_healthy
restart: unless-stopped
networks:
- spring-network
mysql:
image: mysql:8.0
container_name: spring-mysql
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: mydb
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
networks:
- spring-network
volumes:
mysql_data:
networks:
spring-network:
driver: bridge
Run with Docker Compose
# Start all services
docker-compose up -d
# View logs
docker-compose logs -f
# Stop all services
docker-compose down
# Stop and remove volumes
docker-compose down -v
Step 7: Optimize Your Docker Image
Use Layered Jars (Spring Boot 2.3+)
Update your pom.xml:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
</plugins>
</build>
Optimized Dockerfile with Layers
FROM eclipse-temurin:17-jre-alpine AS builder
WORKDIR /app
COPY target/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# Copy layers in order of least to most frequently changing
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
EXPOSE 8080
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
Step 8: Push to Docker Registry
Docker Hub
# Login
docker login
# Tag your image
docker tag spring-boot-app:latest yourusername/spring-boot-app:latest
# Push
docker push yourusername/spring-boot-app:latest
Private Registry
# Tag with registry URL
docker tag spring-boot-app:latest registry.example.com/spring-boot-app:latest
# Push
docker push registry.example.com/spring-boot-app:latest
Step 9: Deploy to Production
Using Docker
# Pull latest image
docker pull yourusername/spring-boot-app:latest
# Run container
docker run -d \
--name spring-app \
-p 80:8080 \
--restart unless-stopped \
yourusername/spring-boot-app:latest
Using Docker Compose in Production
# Pull and start services
docker-compose -f docker-compose.prod.yml up -d
# View logs
docker-compose -f docker-compose.prod.yml logs -f app
Best Practices
- Use Multi-Stage Builds: Reduces final image size significantly
- Don't Run as Root: Create a non-root user for security
- Use Specific Base Image Tags: Avoid
latesttag in production - Implement Health Checks: Add Spring Boot Actuator for monitoring
- Use Environment Variables: Never hardcode sensitive information
- Optimize Layers: Order Dockerfile commands from least to most frequently changing
- Use .dockerignore: Exclude unnecessary files from build context
- Version Your Images: Use semantic versioning for Docker images
Adding Spring Boot Actuator
Add to pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Update application.properties:
management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=always
Update Dockerfile with health check:
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:8080/actuator/health || exit 1
Troubleshooting
Container Exits Immediately
# Check logs
docker logs spring-boot-app
# Run interactively
docker run -it spring-boot-app:latest /bin/sh
Connection Refused
- Ensure the application binds to
0.0.0.0, notlocalhost - Check port mappings:
-p host:container - Verify firewall rules
Out of Memory
# Increase memory limit
docker run -m 1g spring-boot-app:latest
# Or set Java heap size
docker run -e JAVA_OPTS="-Xmx512m -Xms256m" spring-boot-app:latest
Conclusion
You've successfully dockerized your Spring Boot application! This setup provides a solid foundation for deploying your application in any environment that supports Docker.