Safeguard
Software Supply Chain•

What are the best practices for building Java containers with Docker?

Build Java images with multi-stage builds, a slim pinned base, a non-root user and a dropped-privileges runtime. Then scan the dependencies inside the jar.

Safeguard Security Team
4 min read

For a Java container, build in one stage and run in another, use a small base image pinned by digest, run as a non-root user, and start the container with dropped privileges. Then scan the image and the libraries inside your jar, because most findings live there.

Why use a multi-stage build?

Docker's build guidance says multi-stage builds separate the build from the final output so the image holds only what the application needs to run. For Java, that means Maven or Gradle and the full JDK stay in the first stage. The final stage copies only the built jar onto a runtime image.

Constructed example: a service image built in one stage carries the compiler, the build cache and test dependencies, and weighs several times what the app needs. The same service split into two stages ships a jar and a runtime. There is less to patch, and fewer tools for an attacker to use if the container is breached.

Which base image and how do you pin it?

Docker recommends trusted, minimal base images such as Docker Official Images, Verified Publisher images or Docker-Sponsored Open Source images, with a slimmer image for production than for building and testing.

Tags are mutable, so FROM someimage:21 can change under you. Adding an @sha256: digest guarantees the same image on each build. The tradeoff, which Docker states, is that you stop getting automatic base updates, so use a dependency update tool to propose new digests. Rebuild regularly with the pull flag so you pick up patched bases.

How do you stop running as root?

Create a user and group in the Dockerfile and switch with USER. Docker suggests an explicit UID and GID for reproducibility. A Java service that only listens on a high port and writes to a temp directory needs no privileges.

What belongs in the run command?

The OWASP Docker Security Cheat Sheet lists runtime rules:

  • Drop all capabilities with the cap-drop all option and add back only what is needed. Never run privileged containers.
  • Set the no-new-privileges security option to block escalation through setuid binaries.
  • Use a read-only filesystem with a tmpfs mount for temporary writes.
  • Never expose the Docker socket. Access to it is equivalent to root on the host.
  • Use Docker secrets, not values baked into images or commands.

Java apps usually write only to a temp directory, so a read-only root filesystem is practical.

What about the Java dependencies?

The base image is the smaller risk. A typical service has dozens of libraries pulled in through Maven or Gradle, and a vulnerable one rides into the image inside the jar. Image scanners often see a jar as one opaque file or read it shallowly. Scan the dependency tree at build time, and generate a software bill of materials so you can answer "are we affected?" within minutes next time an advisory lands.

How to check or apply this

  1. Split your Dockerfile into a build stage and a runtime stage.
  2. Pin the runtime base by digest and schedule digest updates.
  3. Add a .dockerignore so source history, local config and keys never enter the build context.
  4. Add USER with a fixed UID.
  5. Run with dropped capabilities, no-new-privileges and a read-only filesystem.
  6. Scan the image and the Maven or Gradle dependency tree in CI, and fail on findings you can reach.
  7. Rebuild weekly, even without code changes.

Where does Safeguard fit?

Safeguard scans dependencies, produces SBOM data and uses reachability analysis to separate libraries your code calls from those it never touches. The Gold free scan at gold.safeguard.sh/scan checks up to 100 dependencies in the browser.

Quick check

Why pin a base image by digest?

Tags are mutable and can point to different content later. A digest guarantees identical content on each build.

Is a read-only filesystem realistic for Java?

Usually yes. Mount a tmpfs for temporary files and keep the rest of the filesystem read-only.

Sources

Last reviewed: 18 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.