Back to Blog
Guide8 min read2026-05-01

Spring Boot Production Deployment: Docker and Cloud Platforms

A step-by-step guide to containerizing and deploying Spring Boot applications to production with Docker, health checks, and a cloud PaaS.

Spring Boot Production Deployment: Docker and Cloud Platforms

Spring Boot is the dominant Java framework for building REST APIs and microservices. Its opinionated defaults and embedded Tomcat server make it easy to develop locally, but deploying to production requires proper containerization, externalized configuration, and health check integration.

This guide covers deploying a Spring Boot application to production on [PandaStack](https://pandastack.io).

Spring Boot's Production-Ready Features

Spring Boot ships with Spring Actuator, which provides built-in health check endpoints (/actuator/health), metrics, and application info out of the box. These are invaluable in production.

Add Actuator to your pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
</dependency>

Production Application Properties

Use Spring profiles to separate production configuration from development:

# src/main/resources/application-production.yml
spring:
  datasource:
    url: ${DATABASE_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false

server:
  port: ${PORT:8080}

management:
  endpoints:
    web:
      exposure:
        include: health,info
  endpoint:
    health:
      show-details: never

Exposing only health and info actuator endpoints in production avoids leaking sensitive application internals.

Multi-Stage Dockerfile

# Stage 1: Build
FROM maven:3.9-eclipse-temurin-21 AS builder

WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -q

COPY src ./src
RUN mvn package -DskipTests -q

# Stage 2: Run
FROM eclipse-temurin:21-jre-alpine

WORKDIR /app

RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser
USER appuser

COPY --from=builder /app/target/*.jar app.jar

EXPOSE 8080

ENV SPRING_PROFILES_ACTIVE=production

ENTRYPOINT ["java",   "-XX:MaxRAMPercentage=75.0",   "-XX:+UseContainerSupport",   "-Djava.security.egd=file:/dev/./urandom",   "-jar", "app.jar"]

Key JVM flags for containers:

  • -XX:+UseContainerSupport — makes the JVM respect container memory limits (not host RAM)
  • -XX:MaxRAMPercentage=75.0 — allocates 75% of container memory to the JVM heap
  • -Djava.security.egd=file:/dev/./urandom — speeds up startup by using a non-blocking random source

Configuring pandastack.json

{
  "type": "container",
  "healthCheckPath": "/actuator/health"
}

Spring Actuator's /actuator/health endpoint automatically checks database connectivity and returns {"status":"UP"} when everything is healthy. PandaStack will only route traffic to your container once this endpoint confirms the app is fully started.

Externalizing Configuration

Spring Boot reads environment variables and maps them to properties automatically. Set the following in the PandaStack dashboard at [dashboard.pandastack.io](https://dashboard.pandastack.io):

SPRING_PROFILES_ACTIVE=production
DATABASE_URL=jdbc:postgresql://host:5432/mydb
DB_USERNAME=myapp_user
DB_PASSWORD=your-secure-password
JWT_SECRET=your-jwt-signing-secret
PORT=8080

Spring Boot maps DATABASE_URL to spring.datasource.url, DB_USERNAME to spring.datasource.username, and so on when you reference them via ${ENV_VAR} in your YAML.

Provisioning a Database

Provision a managed PostgreSQL instance from the Databases section of [dashboard.pandastack.io](https://dashboard.pandastack.io). Use the connection details to populate DATABASE_URL, DB_USERNAME, and DB_PASSWORD.

Set spring.jpa.hibernate.ddl-auto=validate in production and manage schema changes via Flyway or Liquibase migrations:

<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>
-- src/main/resources/db/migration/V1__create_users.sql
CREATE TABLE users (
    id BIGSERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

Graceful Shutdown

Enable Spring Boot's graceful shutdown in your configuration:

server:
  shutdown: graceful
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

This allows in-flight requests to complete before the application stops, important during rolling deployments.

GitHub Integration and Deployment

Connect your GitHub repository from the PandaStack dashboard and select your deployment branch. Every push triggers an automatic Docker build and deployment. For CLI-based deployments:

npm install -g @pandastack/cli
panda deploy

Production Checklist

  • SPRING_PROFILES_ACTIVE=production is set
  • JVM container support flags (-XX:+UseContainerSupport) are configured
  • /actuator/health returns {"status":"UP"}
  • pandastack.json points healthCheckPath to /actuator/health
  • Database migrations managed by Flyway or Liquibase
  • Graceful shutdown enabled with appropriate timeout
  • All secrets stored as environment variables

Configuring Spring Security for Production

By default, Spring Security in production should disable default credential headers and enforce HTTPS. Add these properties:

# application-production.yml
server:
  servlet:
    session:
      cookie:
        secure: true
        same-site: strict
  forward-headers-strategy: native

spring:
  security:
    headers:
      hsts:
        enabled: true
        max-age-seconds: 31536000

These settings ensure session cookies are secure-only, HSTS headers are sent to enforce HTTPS, and Spring correctly reads forwarded headers from PandaStack's reverse proxy when determining the original request scheme and host.

Logging in Production

Spring Boot's default logging outputs to the console via Logback. In production, structured JSON logging makes logs searchable in the PandaStack dashboard. Add the Logstash Logback encoder:

<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>7.4</version>
</dependency>
# application-production.yml
logging:
  level:
    root: WARN
    com.yourcompany: INFO
  pattern:
    console: "%d{ISO8601} %highlight(%-5level) [%blue(%t)] %yellow(%C{1}): %msg%n%throwable"

Always set root log level to WARN in production and selectively enable INFO for your own packages. Verbose DEBUG logging from Spring internals adds noise and consumes storage without providing actionable signal.

For full documentation, visit [docs.pandastack.io](https://docs.pandastack.io).

Ready to deploy?

Start free on PandaStack — no credit card required.

Start free on PandaStack

More in Guide

Browse all Guide articles →

See also