Docker Best Practices for Java Applications
Docker is a powerful tool for running Java applications, but creating an efficient and secure Dockerfile requires more than copying examples from the internet. In this guide, you will learn Docker best practices for Java applications, from optimizing images to securing containers, while also exploring automation strategies supported by ZippyOPS.

Introduction to Docker Best Practices for Java
Writing Dockerfiles may seem simple. However, production environments demand security, efficiency, and maintainability. Following Docker’s official guidelines ensures your containers run smoothly. Think of it like writing clean code: knowing the syntax is not enough—you need best practices to create robust containers.
In this article, we demonstrate how to transform a basic Dockerfile for a Java application into a production-ready, secure, and optimized image.
Here is a common starting Dockerfile for a Java application:
FROM eclipse-temurin:17
RUN mkdir /opt/app
ARG JAR_FILE
ADD target/${JAR_FILE} /opt/app/app.jar
CMD ["java", "-jar", "/opt/app/app.jar"]
This Dockerfile performs these actions:
- FROM: Uses Eclipse Temurin JDK 17 as the base image.
- RUN: Creates the
/opt/appdirectory. - ARG: Sets the
JAR_FILEargument to avoid hardcoding the JAR name. - ADD: Copies the JAR file into the container.
- CMD: Starts the Java application.
Later sections will refine this Dockerfile using proven best practices.
Prerequisites for Docker Best Practices for Java
Before starting, ensure you have:
- Basic knowledge of Linux commands
- Familiarity with Java and Spring Boot
- Basic understanding of Docker and containerization
Sample Java Application
We use a simple Spring Boot application with the Spring Web dependency. Run it locally with:
$ mvn spring-boot:run
For building the Docker image, we use the dockerfile-maven-plugin fork:
<plugin>
<groupId>com.xenoamess.docker</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.25</version>
<configuration>
<repository>mydeveloperplanet/dockerbestpractices</repository>
<tag>${project.version}</tag>
<buildArgs>
<JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
Build the project and Docker image with:
$ mvn clean verify
$ mvn dockerfile:build
Run the container:
$ docker run --name dockerbestpractices mydeveloperplanet/dockerbestpractices:0.0.1-SNAPSHOT
Test the Hello endpoint:
$ curl http://<container-ip>:8080/hello
Hello Docker!
1. Choose the Right Base Image
The base image significantly affects your container size and security. Instead of using eclipse-temurin:17, consider the lighter 17-jre-alpine image. This reduces size from 235MB to 59MB and removes unnecessary JDK tools that could pose security risks.
Always use explicit version tags like 17.0.5_8-jre-alpine@sha256:<hash> for reproducibility. Using a SHA256 hash ensures that a wrong or compromised image will fail to build.
FROM eclipse-temurin:17.0.5_8-jre-alpine@sha256:02c04793fa49ad5cd193c961403223755f9209a67894622e05438598b32f210e
Reducing image size improves distribution speed and security.
2. Avoid Running as Root
By default, containers run as root, creating potential security vulnerabilities. Instead, create a system user and group:
RUN addgroup --system javauser && adduser -S -s /usr/sbin/nologin -G javauser javauser
RUN chown -R javauser:javauser /opt/app
USER javauser
Running as a non-root user minimizes risks and aligns with industry best practices.
3. Use WORKDIR
Instead of repeatedly specifying the full path, use WORKDIR:
WORKDIR /opt/app
This ensures all subsequent commands run inside /opt/app, simplifying your Dockerfile and improving readability.
4. Prefer ENTRYPOINT Over CMD
For executable Java containers, ENTRYPOINT ensures your application always runs correctly:
ENTRYPOINT ["java", "-jar", "app.jar"]
Unlike CMD, it cannot be accidentally overridden at runtime, making container behavior predictable.
5. Use COPY Instead of ADD
COPY is preferred for local files since it only copies files, while ADD has additional features that could lead to unexpected behavior:
COPY target/${JAR_FILE} app.jar
This makes builds more reliable and easier to understand.
6. Use .dockerignore
Prevent unnecessary files from being sent to the Docker daemon. A .dockerignore file can exclude everything except the required JAR:
**/**
!target/*.jar
This speeds up builds and reduces image size, especially in projects with many dependencies like Node modules.
7. Run Docker Daemon Rootless
Running Docker as root is unnecessary and insecure. Docker v20.10 supports rootless mode, or you can use Podman for daemonless, non-root container management.
ZippyOPS Solutions for Containerized Java Applications
At ZippyOPS, we provide consulting, implementation, and managed services for DevOps, DevSecOps, DataOps, Cloud, Automated Ops, AIOps, MLOps, Microservices, Infrastructure, and Security. Leveraging Docker best practices is a core part of our solutions. Explore our offerings:
Our expertise helps organizations build secure, scalable, and efficient containerized environments.
Conclusion for Docker Best Practices for Java
Applying Docker best practices for Java ensures smaller, faster, and more secure containers. Key takeaways include:
- Choose lightweight, explicit base images
- Avoid running as root
- Use WORKDIR, ENTRYPOINT, and COPY
- Implement a
.dockerignore - Consider rootless Docker or Podman
For end-to-end container consulting, deployment, and management, contact ZippyOPS at sales@zippyops.com.



