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

bash
mvn clean package -DskipTests

Artifact:

text
target/demo-0.0.1-SNAPSHOT.jar

Run:

bash
java -jar target/demo-0.0.1-SNAPSHOT.jar

Override profile:

bash
java -jar target/demo.jar --spring.profiles.active=prod

Code explanation:

  • Boot repackages with spring-boot-maven-plugin—embeds dependencies ("fat jar")

Layered JAR (Docker Cache Friendly)

pom.xml:

xml
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <layers>
            <enabled>true</enabled>
        </layers>
    </configuration>
</plugin>

Extract layers:

bash
java -Djarmode=tools -jar target/demo.jar extract --layers --destination extracted

Dockerfile copies dependencies/, spring-boot-loader/, snapshot-dependencies/, application/ separately—rebuild only changes app layer when code changes.

Dockerfile (Simple)

dockerfile
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:

bash
docker build -t demo-app:1.0 .
docker run -p 8080:8080 -e SPRING_PROFILES_ACTIVE=prod demo-app:1.0

Externalize Configuration

Never bake secrets into image:

yaml
spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}

Kubernetes/Docker env:

bash
-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:

nginx
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

text
git push → CI pipeline
  ├── mvn test
  ├── mvn package
  ├── docker build & push
  └── deploy to staging/prod (kubectl, ssh, PaaS)

GitHub Actions sketch:

yaml
- 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

bash
java -Xms512m -Xmx512m -XX:+UseG1GC -jar app.jar

Set heap from metrics—not guesswork. Enable GC logging when tuning.

Graceful shutdown:

yaml
server:
  shutdown: graceful

FAQ

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.