Start Learning
Javaneer
Back to stage
Module 8·Production & Modern Spring

Packaging & Deployment

From jar to running container: layered Docker images with buildpacks, the twelve-factor config model, and how a Spring Boot app goes to production.

15 min readIntermediate
On this page

You've built a real service - secured, tested, observable. The last step is getting it running on a server, reproducibly, in a way you can update without downtime. This lesson connects the dots from jar to container to deployment, and the config discipline that makes the same build run anywhere.

One artifact, promoted everywhere

Spring Boot packages your whole app - code, dependencies, and an embedded server - into a single executable jar:

./mvnw package
java -jar target/bookvault-0.0.1-SNAPSHOT.jar     # runs, no external server needed

The discipline that makes this jar production-ready is the twelve-factor idea you've met throughout the Spring path: build the artifact once, and promote that exact artifact through dev, staging, and prod - never rebuild per environment. Everything that differs between environments comes from outside the jar:

# same jar, different environment - config via env vars and profiles, not new builds
SPRING_PROFILES_ACTIVE=prod \
DATABASE_URL=jdbc:postgresql://db/bookvault \
JWT_SECRET=$PROD_SECRET \
java -jar bookvault.jar

Because config lives in the environment, not the code, the jar is immutable and reproducible - the same bytes you tested are the bytes you run.

Into a container

Servers run containers now, not loose jars - a container bundles the jar and a JRE and OS libraries into one portable image that runs identically on any host. You rarely need to hand-write a Dockerfile: Spring Boot builds an optimized image with Cloud Native Buildpacks, no Dockerfile at all:

./mvnw spring-boot:build-image      # produces a container image via buildpacks

If you do write a Dockerfile, the key technique is layering. Spring Boot splits the jar into layers that change at different rates - dependencies (rarely) vs. your classes (every build) - so Docker caches the unchanged dependency layers and only the small application layer rebuilds:

# build stage: produce the jar
FROM eclipse-temurin:21-jdk AS build
WORKDIR /app
COPY . .
RUN ./mvnw -q package -DskipTests

# run stage: a slim JRE image, jar unpacked into cached layers
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ['java','-jar','app.jar']
Shipping containers

Before standardized shipping containers, moving goods meant repacking cargo for each truck, crane, and ship - slow and error-prone. The container fixed the interface: whatever's inside, it stacks, lifts, and ships the same way everywhere. A software container does the same for your app - the host doesn't care what's inside; it runs any container identically. Your jar, its JRE, and its libraries travel together, so 'works on my machine' becomes 'works on every machine'.

Health checks close the loop

Deployment platforms (Kubernetes, ECS) use the Actuator health endpoint from the observability lesson as liveness/readiness probes: they only route traffic to instances reporting UP, and restart ones that don't. That's what enables rolling, zero-downtime deploys - start new instances, wait for them to report healthy, then retire the old ones. Observability and deployment connect here.

Never bake secrets into the image

A container image is shared and stored in registries - anything inside it (a hard-coded password, a JWT secret) leaks with it. Inject secrets at runtime via environment variables or a secrets manager, exactly as twelve-factor prescribes. The same immutable image runs in every environment; only the injected config differs.

One build, three environments

BookVault needs to run in dev (H2), staging, and prod (each a different PostgreSQL and a different JWT secret). A teammate suggests building three jars, one per environment, with the values compiled in. Explain why that's the wrong model and how the twelve-factor / container approach achieves it with a single artifact.

What is the twelve-factor / Spring Boot approach to building and configuring a deployable app?

Key takeaways

  • Spring Boot packages the app as one executable jar with an embedded server - java -jar and it runs.
  • Twelve-factor: build the artifact once and promote it through environments; never rebuild per environment.
  • Environment-specific values (DB URLs, secrets, profiles) come from outside the jar, keeping it immutable and reproducible.
  • Containers bundle the jar + JRE for identical runs anywhere; Spring Boot builds optimized, layered images via buildpacks (no Dockerfile needed).
  • Layering caches unchanged dependency layers so only your small application layer rebuilds.
  • Actuator health endpoints act as liveness/readiness probes, enabling rolling zero-downtime deploys; never bake secrets into the image.
Was this lesson helpful?
Edit this page on GitHub