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: neverExposing 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=8080Spring 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: 30sThis 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 deployProduction Checklist
SPRING_PROFILES_ACTIVE=productionis set- JVM container support flags (
-XX:+UseContainerSupport) are configured /actuator/healthreturns{"status":"UP"}pandastack.jsonpointshealthCheckPathto/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: 31536000These 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).