Back to Blog
Tutorial11 min read2026-07-05

How to Deploy Spring Boot with PostgreSQL

Deploy a Spring Boot service with an auto-wired managed Postgres: layered or buildpack images, HikariCP pool tuning, Flyway migrations, actuator health checks, and graceful shutdown.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

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.0

Step 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 bootBuildImage

If 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.Driver

Note 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=10000

Math: 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=30s

The orchestrator uses /actuator/health/liveness and /readiness to manage rollout.

Step 7: Deploy

Connect your repo and push:

git push origin main

The 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=75

On 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}, address 0.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
  • [ ] MaxRAMPercentage set 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.

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also