Spring Boot, containerized cleanly
Spring Boot is the default for a huge share of production Java. It packages an embedded server (Tomcat/Netty) into a runnable jar, which makes containerizing straightforward — and it has first-class support for building OCI images without a Dockerfile. Add a managed Postgres with auto-wired credentials and you have a tidy full-stack deploy.
Step 1: Bind to the injected port
Spring reads SERVER_PORT / server.port. Map it to the platform's PORT:
# application.properties
server.port=${PORT:8080}
server.address=0.0.0.0Step 2: Build an image without a Dockerfile
Spring Boot's build plugins create optimized, layered OCI images via Cloud Native Buildpacks — no Dockerfile required:
# Maven
./mvnw spring-boot:build-image
# Gradle
./gradlew bootBuildImageIf you prefer a Dockerfile, use the layered jar for good caching:
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]On PandaStack, buildpacks auto-detect the project, or it builds your Dockerfile — both paths work.
Step 3: Wire the managed PostgreSQL
Provision a managed PostgreSQL (14.x or 16.x). Spring's datasource reads JDBC properties. PandaStack injects connection details when you attach the database — map them in:
spring.datasource.url=${JDBC_DATABASE_URL}
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASSWORD}
spring.datasource.driver-class-name=org.postgresql.DriverNote Spring's JDBC URL format (jdbc:postgresql://host:5432/db) differs from the libpq postgres:// form, so set JDBC_DATABASE_URL explicitly from the injected host/port/dbname.
Step 4: Tune HikariCP
Spring Boot uses HikariCP by default. The single most important setting is pool size — it must stay under your tier's connection limit, accounting for the number of replicas:
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=2
spring.datasource.hikari.connection-timeout=10000Math: replicas x maximum-pool-size <= tier limit (50 free, 300 Pro, 1000 Premium). Two replicas x pool 10 = 20 connections — fine on free.
Step 5: Migrations with Flyway
Add the Flyway dependency and place migrations in src/main/resources/db/migration. Spring runs them at startup:
-- V1__init.sql
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ DEFAULT now()
);For multi-replica safety, Flyway takes a lock so concurrent startups won't double-apply, but a dedicated migration job is the cleanest pattern at scale.
Step 6: Health checks and graceful shutdown
Add spring-boot-starter-actuator for health endpoints, and enable graceful shutdown so in-flight requests drain on redeploy:
management.endpoint.health.probes.enabled=true
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30sThe orchestrator uses /actuator/health/liveness and /readiness to manage rollout.
Step 7: Deploy
Connect your repo and push:
git push origin mainThe build runs in a rootless BuildKit K8s Job pod, the image goes to Artifact Registry, and Helm deploys it. Live logs stream from self-hosted Elasticsearch, automatic SSL covers your custom domain, and server-side metrics arrive without instrumenting anything.
Step 8: JVM memory in containers
Modern JVMs are container-aware and respect cgroup limits, but be explicit on small tiers:
JAVA_TOOL_OPTIONS=-XX:MaxRAMPercentage=75On the free tier (0.25 CPU / 512MB), Spring Boot is tight — consider a compute or memory tier for real workloads, or evaluate Spring's AOT/native options for lower footprint.
Production checklist
- [ ]
server.port=${PORT}, address0.0.0.0 - [ ] Image via buildpacks or layered Dockerfile
- [ ] Datasource wired from injected DB details
- [ ] HikariCP pool x replicas under tier limit
- [ ] Flyway migrations versioned
- [ ] Actuator health + graceful shutdown
- [ ]
MaxRAMPercentageset for the tier
References
- [Spring Boot — Container Images](https://docs.spring.io/spring-boot/reference/packaging/container-images/index.html)
- [HikariCP configuration](https://github.com/brettwooldridge/HikariCP#configuration-knobs-baby)
- [Flyway documentation](https://documentation.red-gate.com/flyway)
- [Spring Boot Actuator](https://docs.spring.io/spring-boot/reference/actuator/index.html)
---
Spring Boot plus a managed Postgres is a clean, reproducible deploy once the datasource and pool are sized right. Push it to PandaStack's [free tier](https://dashboard.pandastack.io), attach a managed Postgres, and let the platform handle the build and TLS.