Introduction: Why Docker is Important for a Java Developer?
Modern Java development rarely goes without containerization. Docker allows you to package an application together with all its dependencies, JVM, and configuration into an isolated container. This solves the "it works on my machine" problem and simplifies CI/CD. In this article, we will cover the full cycle: from installing Docker to optimizing images for production.
1. Docker Basics for Java
1.1 What is Docker and Why Does It Matter for Java Projects?
Docker is a platform for creating, delivering, and running applications in containers. This is especially relevant for Java:
- JVM version isolation — you can use Java 11 for one service and Java 17 for another on the same host.
- Reproducibility — the image contains the exact version of the JDK, libraries, and system utilities.
- Scalability — easily run multiple instances via Kubernetes or Docker Swarm.
1.2 Installing Docker
To start, install Docker Desktop (Windows/Mac) or Docker Engine (Linux). Verify the installation:
docker --versiondocker info2. Creating a Dockerfile for a Java Application
Let's consider a simple Spring Boot application. Create a Dockerfile in the project root.
2.1 Basic Dockerfile
# Use the official OpenJDK imageFROM openjdk:17-jdk-alpine
# Set the working directoryWORKDIR /app
# Copy the JAR fileCOPY target/myapp.jar app.jar
# Expose the portEXPOSE 8080
# Startup commandENTRYPOINT ["java", "-jar", "app.jar"]2.2 Building and Running the Container
# Build the imagedocker build -t my-java-app .
# Run the containerdocker run -p 8080:8080 my-java-appAfter running, the application will be available at http://localhost:8080.
3. Optimizing Docker Images for Java
Large images slow down deployment. Let's look at key optimization techniques.
3.1 Multi-stage Builds
This allows separating the build stage from the runtime stage. Only the JAR ends up in the final image, without Maven/Gradle and source code.
# Build stageFROM maven:3.8.5-openjdk-17 AS buildWORKDIR /appCOPY pom.xml .RUN mvn dependency:go-offlineCOPY src ./srcRUN mvn package -DskipTests
# Runtime stageFROM openjdk:17-jdk-alpineWORKDIR /appCOPY --from=build /app/target/myapp.jar app.jarEXPOSE 8080ENTRYPOINT ["java", "-jar", "app.jar"]3.2 Layer Optimization
Docker caches layers. Separate data copying: first dependencies (change rarely), then code (changes frequently).
FROM maven:3.8.5-openjdk-17 AS buildWORKDIR /app# Copy only POM and download dependenciesCOPY pom.xml .RUN mvn dependency:go-offline# Copy sources and buildCOPY src ./srcRUN mvn package -DskipTests
FROM openjdk:17-jdk-alpineWORKDIR /appCOPY --from=build /app/target/*.jar app.jarEXPOSE 8080ENTRYPOINT ["java", "-jar", "app.jar"]3.3 Using jlink to Create a Minimal JRE
If you are using Java 9+, you can create a custom JRE with only the required modules.
FROM openjdk:17-slim AS jre-buildRUN jlink --add-modules java.base,java.logging,java.sql \ --output /jre \ --strip-debug \ --no-man-pages \ --no-header-files
FROM alpine:latestCOPY --from=jre-build /jre /jreCOPY target/app.jar /app.jarENTRYPOINT ["/jre/bin/java", "-jar", "/app.jar"]Such an image can weigh less than 50 MB instead of 300+ MB.
4. Docker Compose for Java Microservices
If you have multiple services (e.g., Spring Boot + PostgreSQL), use docker-compose.yml.