Introduction to Docker and Java
Modern Java application development is unthinkable without containerization. Docker is a platform for packaging, delivering, and running applications in isolated environments (containers). For Java developers, Docker solves the eternal problem: "It works on my machine!". A container guarantees that your application will run identically on your local machine, development server, and in production.
In this article, we will explore how to containerize Java applications using Docker: from a simple Dockerfile to optimizing images for microservice architecture. All examples are written in Java and ready to use.
Docker Basics for Java Applications
What is a Docker Image and Container?
A Docker image is a template (read-only) containing everything needed to run an application: JVM, libraries, code, and configurations. A container is a running instance of an image. For Java, this means you don't need to manually install JDK on the server — the JVM will already be inside the container.
Creating a Simple Dockerfile for Java
Let's look at a basic example. We have a simple Java application (e.g., HelloWorld.java).
// HelloWorld.javapublic class HelloWorld { public static void main(String[] args) { System.out.println("Hello from Docker and Java!"); }}Let's create a Dockerfile:
# Use the official OpenJDK imageFROM openjdk:11-jre-slim
# Set the working directoryWORKDIR /app
# Copy the compiled jar or class filesCOPY HelloWorld.class .
# Run commandCMD ["java", "HelloWorld"]Building and running:
# Build the imagedocker build -t hello-java .
# Run the containerdocker run hello-javaOutput: Hello from Docker and Java!
Containerizing Spring Boot Applications
For real projects (e.g., Spring Boot), the process becomes more complex. Let's look at a typical microservice.
Multi-stage Build
This is a best practice for Java: first build the application with the full JDK, then copy the result into a lightweight JRE image.
# Stage 1: BuildFROM maven:3.8.5-openjdk-11 AS buildWORKDIR /appCOPY pom.xml .RUN mvn dependency:go-offlineCOPY src ./srcRUN mvn clean package -DskipTests
# Stage 2: RunFROM openjdk:11-jre-slimWORKDIR /appCOPY --from=build /app/target/*.jar app.jarEXPOSE 8080ENTRYPOINT ["java", "-jar", "app.jar"]Explanation:
- build — uses Maven and the full JDK for compilation.
- Final image — contains only JRE and the ready jar file. The image size is reduced by 2-3 times.
Optimizing Layer Caching
Docker caches each layer (instruction). To speed up builds, place rarely changed dependencies at the beginning:
FROM maven:3.8.5-openjdk-11 AS buildWORKDIR /app# Cache dependenciesCOPY pom.xml .RUN mvn dependency:go-offline# Now copy the codeCOPY src ./srcRUN mvn clean packageNow, if you only change the code, Docker will only rebuild the last layers, while dependencies will be taken from the cache.
Working with Environment Variables and Profiles
Passing Parameters to a Java Application
Docker allows you to flexibly configure your application through environment variables. This is especially convenient for Spring Boot.
docker run -e SPRING_PROFILES_ACTIVE=prod -e DB_URL=jdbc:postgresql://db:5432/mydb my-appIn the Dockerfile, you can set default values:
ENV SPRING_PROFILES_ACTIVE=devENV DB_URL=jdbc:h2:mem:testdbExample docker-compose for Java + Database
For microservices, it's convenient to use docker-compose.yml:
version: '3.8'services: app: bu