Dockerfile Builder for Java Spring Boot

Generate a production Dockerfile for Spring Boot microservices

Create a multi-stage Dockerfile with a Maven or Gradle build stage and a slim JRE runtime stage for Spring Boot apps, including a non-root user and an Actuator-based health check. Runs entirely in your browser. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

Why split the build and runtime into two stages?

The build stage needs the full JDK plus Maven or Gradle to compile and package the jar, which is large. The runtime stage only needs a JRE to run the jar, so copying just the built jar into a slim JRE image produces a much smaller and more secure final image.

A Dockerfile builder for Spring Boot that produces a clean multi-stage Dockerfile for Java microservices. You choose Maven or Gradle, a JDK, a port, and whether to wire in an Actuator health check, and it emits a build that compiles your jar in one image and runs it in a slim JRE image as a non-root user.

How it works

The build stage uses a full image that contains both the JDK and your build tool. For Maven it runs mvn dependency:go-offline after copying only pom.xml, so dependency resolution is cached as its own layer, then runs mvn package -DskipTests. For Gradle it resolves dependencies, then runs bootJar. Either way the resulting fat jar is copied to a predictable path, app.jar.

The runtime stage starts from eclipse-temurin:<jdk>-jre-alpine — a slim image with only the Java runtime. It creates a dedicated spring user, copies the jar with the right ownership, switches to that user with USER, and starts the app with MaxRAMPercentage so the JVM sizes its heap against the container limit. When enabled, the HEALTHCHECK polls /actuator/health and looks for the UP status.

Tips and notes

  • Tests are skipped in the image build (-DskipTests / -x test) on purpose — run your test suite in CI before building the image, not inside the container build.
  • For even faster startup and smaller images you can extract the Spring Boot layered jar with java -Djarmode=layertools -jar app.jar extract; this builder keeps the simpler single-jar form, which is correct for the vast majority of services.
  • Always pin an LTS JDK (21, 17, or 11) so your runtime matches what you tested against.
  • Make sure spring-boot-starter-actuator is on the classpath and management.endpoint.health.probes.enabled=true is set before relying on the health check.

What the generated Dockerfile looks like

A Maven build targeting JDK 21 produces something like:

# Build stage
FROM eclipse-temurin:21-jdk AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline -q
COPY src ./src
RUN mvn package -DskipTests -q

# Runtime stage
FROM eclipse-temurin:21-jre-alpine
RUN addgroup -S spring && adduser -S spring -G spring
WORKDIR /app
COPY --from=builder --chown=spring:spring /build/target/*.jar app.jar
USER spring
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s \
  CMD wget -qO- http://localhost:8080/actuator/health | grep UP || exit 1
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]

The dependency-caching pattern is the single biggest time-saver in repeated builds. Because pom.xml is copied and dependencies resolved before any source is touched, Docker caches that layer. As long as pom.xml does not change, a code-only rebuild skips the slow download step entirely.

Why JRE instead of JDK at runtime

The full JDK includes the compiler (javac), debugging tools, jshell, and documentation — none of which a deployed application needs. The JRE contains only what the JVM needs to execute .class files. Using the JRE image in the runtime stage produces a meaningfully smaller final image and removes compiler tools that could be used by an attacker who gained container access.

eclipse-temurin is the open-source distribution of Eclipse Adoptium builds (formerly AdoptOpenJDK), the standard community choice for containerised Java. The -alpine suffix adds an even smaller base OS layer at the cost of using musl libc, which works well for most JVM workloads.

Health check configuration

When Actuator health checking is enabled, the container starts with a 60-second grace period (--start-period=60s) to allow Spring Boot to complete its initialisation before health checks begin. Spring Boot typically takes 10–30 seconds depending on the application size, so the 60-second window gives comfortable headroom. In Kubernetes, use separate readinessProbe and livenessProbe entries in your Pod spec rather than the Docker HEALTHCHECK instruction, since Kubernetes has its own health mechanism and does not use the Docker one.