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.
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 neededThe 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.jarBecause 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 buildpacksIf 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']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.
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.