ReviseAlgo Logo

Docker & Kubernetes Deployment

Dockerizing a Spring Boot Application — Optimal Dockerfile

Using layered JAR builds to optimize build caches and minimize run times image sizes.

Interview: Spring Boot deployment optimization. Expect questions on multi-stage build design, layertools extraction mechanics, caching layers, and running containers securely as non-root users.

Last Updated: June 14, 2026 15 min read

Introduction

Packaging a Spring Boot application as a single thick (fat) JAR and copying it directly into a basic Docker image leads to slow deployment pipelines. If you change a single line of Java code, the build invalidates the entire cache layer containing the JAR, forcing Docker to push the entire 100MB+ image to your registry on every minor commit.

Spring Boot solves this by supporting **Layered JARs**. Using a multi-stage Docker build, you can extract the dependencies, resources, and loaders into separate folders. This allows Docker to cache the heavy dependency folders, so code changes only update a tiny application layer, reducing image build and upload times to under a second.

Why It Matters

Using layered JARs reduces dependency download times in cloud build runners, cuts down container storage usage, and secures workloads by running them inside minimal, non-privileged images.

Spring Boot Layertools Extraction

Spring Boot provides a built-in extraction tool called layertools. Running this command splits the fat JAR into four optimized layers:

  1. dependencies: Heavy external libraries (e.g. Spring Framework, Jackson, Hibernate). These change very rarely.
  2. spring-boot-loader: The internal classloaders required to launch the packaged application.
  3. snapshot-dependencies: Snapshot releases of library modules.
  4. application: Your actual custom class files, compiled controllers, and application resources. This changes on every build.

Practical Example

Let's write an optimized multi-stage Dockerfile that extracts and copies these layers sequentially:

Quick Quiz

Q1: Which layer should be copied last in the Dockerfile build sequence to optimize caching?

A) dependencies

B) spring-boot-loader

C) application

D) snapshot-dependencies

Answer: C — The application layer changes on every code edit, so copying it last ensures that the preceding layers are served directly from Docker's build cache.

Scenario-Based Challenge

Production Scenario:

You deploy a Spring Boot container, but a security audit flags it because the container process runs as root. You modify the Dockerfile to run as a custom user spring, but now the container fails to start, throwing a Permission Denied error when trying to write temporary files. How do you resolve this?

View Solution

To secure the container without permission errors:

1. **Verify Directory Ownership:** Ensure the work directory belongs to the non-root user:
RUN chown -R spring:spring /application
2. **Configure Tomcat Temp Directory:** Spring Boot embedded Tomcat writes temp files to /tmp. Set this to a directory the spring user has write access to, or use a volume mount:
ENV TMPDIR=/application/tmp
3. **Mount Temp Directory:** In Kubernetes, mount an emptyDir volume to /tmp. This provides a writeable scratchpad even if the container filesystem is mounted read-only.

Interview Questions

1. What is the JarLauncher entrypoint and why is it preferred over running "java -jar app.jar"?

Using JarLauncher allows Spring Boot to launch the application from the extracted classpath directories (which matches standard production setups) instead of launching a packaged zip file. This improves startup performance and resource consumption, and aligns with Docker's layer cache design.

Production Considerations

Always use specific base image tags (e.g. eclipse-temurin:21.0.2_13-jre-alpine) instead of generic tags like latest to prevent pipeline builds from breaking when base images are updated.