All GraalVM templates

Spring Boot Deployment

Buildpacks, layered jars, Docker images, and native image with GraalVM.

DevZone Tools1,320 copiesUpdated Apr 19, 2026Spring BootSpringJavaGraalVM
# CLAUDE.md — Spring Boot Deployment

## Image strategy

Three options, in order of preference:

1. **Cloud Native Buildpacks** — `mvn spring-boot:build-image` or `gradle bootBuildImage`. Layered, optimized, secure-by-default base.
2. **Layered jar + Dockerfile** — Spring's layered jar separates dependencies from app classes for better caching.
3. **GraalVM native image** — small startup, low memory; complex to debug. Use for serverless / Lambda.

## Buildpacks

- One command, no Dockerfile: `./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=myapp:1.0`.
- Pin the buildpack version: `<imageName>` config + Paketo builder version.
- Configure JVM flags via `BPL_JVM_HEAD_ROOM`, `BPE_*` env vars at runtime.

## Layered jar Dockerfile

```dockerfile
FROM eclipse-temurin:21-jre AS builder
WORKDIR /app
COPY target/*.jar app.jar
RUN java -Djarmode=tools -jar app.jar extract --layers --launcher

FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=builder /app/app/dependencies/ ./
COPY --from=builder /app/app/spring-boot-loader/ ./
COPY --from=builder /app/app/snapshot-dependencies/ ./
COPY --from=builder /app/app/application/ ./
USER nobody
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]
```

- Run as non-root.
- The layer order is the cache order — dependencies change less than app code.

## Native image (GraalVM)

- Add `org.graalvm.buildtools:native-maven-plugin` (or Gradle equivalent).
- `./mvnw -Pnative native:compile` builds the native binary.
- Reflection / dynamic proxies need hints. Spring Boot 3 generates most automatically; provide your own with `RuntimeHintsRegistrar` for edge cases.
- Trade-off: 50–100ms startup, ~50 MB memory; build takes 1–5 minutes; harder to profile.

## JVM tuning

- Container-aware JVM (Java 10+ does this automatically). `MaxRAMPercentage=75` is a good default.
- `-XX:+ExitOnOutOfMemoryError` so the orchestrator restarts you instead of you limping along.
- For Boot 3+, Spring AOT processing reduces startup time even on the JVM. Enable with `spring-boot-maven-plugin` `<process-aot>true</process-aot>`.

## Configuration

- **12-factor**: every config from env vars. No files baked into the image.
- Spring reads env vars natively: `SERVER_PORT=8080` overrides `server.port`. Use `SCREAMING_SNAKE_CASE`.
- Secrets injected by the orchestrator (K8s Secrets, AWS Secrets Manager). Never bake.

## Migrations

- Run **Flyway** or **Liquibase** as a separate one-shot job before the app starts.
- For multi-replica deploys, never run migrations from `app boot` — race condition.
- Schema changes are backward-compatible: add nullable column → deploy → backfill → make NOT NULL in next deploy.

## Health & graceful shutdown

- Configure: `server.shutdown=graceful`, `spring.lifecycle.timeout-per-shutdown-phase=30s`.
- Kubernetes: `terminationGracePeriodSeconds: 35` (slightly more than the shutdown timeout).
- Liveness/readiness split, lock behind a management port.

## CI

- Cache `.m2` / `.gradle` between runs.
- Multi-stage CI: lint → test → build image → integration test against the image → push.
- Sign images with `cosign` if your platform supports it.

## Don't

- Don't run with `--add-opens` flags in production unless you know exactly which library needs them. Each one is a future migration.
- Don't put `application-prod.yml` in version control with secrets in plaintext.
- Don't forget `USER nobody` in your Dockerfile. Root containers are a footgun.
- Don't try to fit a 1 GB JAR into a 256 MB pod. Profile memory before sizing.

Other GraalVM templates