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.
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:
- dependencies: Heavy external libraries (e.g. Spring Framework, Jackson, Hibernate). These change very rarely.
- spring-boot-loader: The internal classloaders required to launch the packaged application.
- snapshot-dependencies: Snapshot releases of library modules.
- 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?
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.