Packaging and Deployment
Introduction
Shipping Spring Boot means building a runnable JAR, externalizing config for each environment, and optionally containerizing with Docker. This chapter covers mvn package, layered JARs, environment variables, reverse proxy with Nginx, and a basic CI/CD outline.
Prerequisites
Build Executable JAR
mvn clean package -DskipTestsArtifact:
target/demo-0.0.1-SNAPSHOT.jarRun:
java -jar target/demo-0.0.1-SNAPSHOT.jarOverride profile:
java -jar target/demo.jar --spring.profiles.active=prodCode explanation:
- Boot repackages with
spring-boot-maven-plugin—embeds dependencies ("fat jar")
Layered JAR (Docker Cache Friendly)
pom.xml:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>Extract layers:
java -Djarmode=tools -jar target/demo.jar extract --layers --destination extractedDockerfile copies dependencies/, spring-boot-loader/, snapshot-dependencies/, application/ separately—rebuild only changes app layer when code changes.
Dockerfile (Simple)
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY target/demo-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]Build and run:
docker build -t demo-app:1.0 .
docker run -p 8080:8080 -e SPRING_PROFILES_ACTIVE=prod demo-app:1.0Externalize Configuration
Never bake secrets into image:
spring:
datasource:
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}Kubernetes/Docker env:
-e DB_URL=jdbc:mysql://db:3306/app
-e DB_USERNAME=app
-e DB_PASSWORD=${SECRET_FROM_VAULT}Use SPRING_APPLICATION_JSON for complex overrides in platforms without many env vars.
Deploy Behind Nginx
Nginx reverse proxy to 127.0.0.1:8080:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}See Proxying Spring Boot and Node.
CI/CD Outline
git push → CI pipeline
├── mvn test
├── mvn package
├── docker build & push
└── deploy to staging/prod (kubectl, ssh, PaaS)GitHub Actions sketch:
- run: mvn -B test package
- run: docker build -t registry/demo:${{ github.sha }} .
- run: docker push registry/demo:${{ github.sha }}Pair with Git and CI/CD.
JVM Flags in Production
java -Xms512m -Xmx512m -XX:+UseG1GC -jar app.jarSet heap from metrics—not guesswork. Enable GC logging when tuning.
Graceful shutdown:
server:
shutdown: gracefulFAQ
jar vs war?
Boot defaults jar + embedded Tomcat; war for external servlet container—legacy.
Two jars in target?
Original jar and *-exec repackaged—run the executable one.
Config inside jar overridden?
External application-prod.yml next to jar or env vars win by precedence.
Blue-green deploy?
Two versions behind load balancer—health check /actuator/health.
systemd service?
ExecStart=/usr/bin/java -jar /opt/app/demo.jar + Restart=on-failure.
Windows service?
Use winsw wrapper or run in container—less common than Linux prod.