Skip to main content

Spring Production Checklist

When moving your Spring application from development to production, there are numerous considerations to ensure your application is secure, performant, and maintainable. This checklist will guide you through the essential steps to prepare your Spring application for a production environment.

Introduction

Deploying a Spring application to production involves more than just copying your code to a server. Production environments have different requirements for security, performance, monitoring, and reliability compared to development environments. Missing critical steps in the deployment process can lead to security vulnerabilities, performance bottlenecks, or maintenance challenges.

This guide provides a comprehensive checklist of items to consider before deploying your Spring application to production. We'll cover configuration management, security considerations, logging and monitoring setup, performance optimization, and more.

Configuration Management

Externalize Configuration

One of the core principles of a production-ready application is to separate configuration from code.

java
// Don't hardcode configuration in your application
@Value("jdbc:mysql://production-db:3306/myapp")
private String dbUrl; // ❌ Bad practice

// Instead, externalize it
@Value("${spring.datasource.url}")
private String dbUrl; // ✅ Good practice

Use Profiles

Spring profiles help manage different configurations for different environments.

java
// application.properties (common settings)
app.name=My Spring Application

// application-dev.properties
spring.datasource.url=jdbc:h2:mem:testdb
logging.level.org.springframework=DEBUG

// application-prod.properties
spring.datasource.url=jdbc:mysql://production-host:3306/mydb
logging.level.org.springframework=WARN

To activate a profile when running your application:

bash
java -jar myapp.jar --spring.profiles.active=prod

Use Environment Variables for Sensitive Information

Never commit sensitive information (passwords, API keys) to version control.

yaml
# application.yaml
spring:
datasource:
url: jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}

Security Considerations

Enable HTTPS

Configure your application to use HTTPS in production:

yaml
# application-prod.yaml
server:
port: 8443
ssl:
key-store: classpath:keystore.p12
key-store-password: ${KEY_STORE_PASSWORD}
key-store-type: PKCS12
key-alias: tomcat

Set Security Headers

Add security headers to your HTTP responses:

java
@Configuration
public class WebSecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.headers()
.contentSecurityPolicy("default-src 'self'")
.and()
.xssProtection()
.and()
.frameOptions().deny()
.and()
.httpStrictTransportSecurity()
.includeSubDomains(true)
.maxAgeInSeconds(31536000);

// other security configurations...

return http.build();
}
}

Secure Cookies

Ensure session cookies are secure:

yaml
# application-prod.yaml
server:
servlet:
session:
cookie:
secure: true
http-only: true
same-site: strict

Use CSRF Protection

Spring Security enables CSRF protection by default. Make sure it's properly configured:

java
@Configuration
public class WebSecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

// other security configurations...

return http.build();
}
}

Logging and Monitoring

Configure Appropriate Log Levels

Adjust your log levels for production:

yaml
# application-prod.yaml
logging:
level:
root: WARN
org.springframework.web: ERROR
com.myapp: INFO
file:
name: /var/log/myapp/application.log

Implement Health Checks

Spring Boot Actuator provides health check endpoints:

xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
yaml
# application-prod.yaml
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: when_authorized

Set Up Metrics Collection

Configure metrics for monitoring:

java
@Configuration
public class MetricsConfig {

@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config()
.commonTags("application", "myapp", "environment", "production");
}
}

Performance Optimization

Enable Caching

Configure caching for frequently accessed data:

java
@Configuration
@EnableCaching
public class CachingConfig {

@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(
new ConcurrentMapCache("users"),
new ConcurrentMapCache("products")
));
return cacheManager;
}
}

Usage example:

java
@Service
public class ProductService {

@Cacheable("products")
public Product getProductById(Long id) {
// This method will be executed only once per id
// Results will be cached
return productRepository.findById(id)
.orElseThrow(() -> new ProductNotFoundException(id));
}
}

Configure Connection Pooling

For database connections:

yaml
# application-prod.yaml
spring:
datasource:
hikari:
maximum-pool-size: 10
minimum-idle: 5
idle-timeout: 120000
connection-timeout: 30000

Tune JVM Settings

Add appropriate JVM flags when running in production:

bash
java -Xms512m -Xmx1024m -XX:+UseG1GC -jar myapp.jar --spring.profiles.active=prod

Error Handling and Resilience

Implement Global Exception Handling

Create a global exception handler:

java
@ControllerAdvice
public class GlobalExceptionHandler {

private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex) {
logger.error("Unexpected error", ex);
ErrorResponse error = new ErrorResponse("Internal Server Error", HttpStatus.INTERNAL_SERVER_ERROR.value());
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(ex.getMessage(), HttpStatus.NOT_FOUND.value());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
}

Add Circuit Breakers for External Services

Using Spring Cloud Circuit Breaker:

xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
java
@Service
public class ExternalServiceClient {

private final CircuitBreakerFactory circuitBreakerFactory;
private final RestTemplate restTemplate;

public ExternalServiceClient(CircuitBreakerFactory circuitBreakerFactory, RestTemplate restTemplate) {
this.circuitBreakerFactory = circuitBreakerFactory;
this.restTemplate = restTemplate;
}

public ExternalData fetchData() {
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("externalService");

return circuitBreaker.run(
() -> restTemplate.getForObject("https://api.external.com/data", ExternalData.class),
throwable -> fallbackData()
);
}

private ExternalData fallbackData() {
// Return fallback data when the external service is unavailable
return new ExternalData("Fallback Data");
}
}

Deployment Configuration

Create a Production Dockerfile

dockerfile
FROM eclipse-temurin:17-jre-alpine

WORKDIR /app

COPY target/myapp.jar app.jar

ENV JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC"
ENV SPRING_PROFILES_ACTIVE="prod"

EXPOSE 8080

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

Configure Health Check Probes

For Kubernetes:

yaml
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 10

readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 5

Real-World Example: E-commerce Application Deployment

Let's see how these concepts apply to a real-world e-commerce application deployment:

  1. Configuration Management: The application would use different database connections for development, testing, and production environments using Spring profiles. Sensitive information like payment API keys would be stored as environment variables.

  2. Security: All customer data endpoints would require authentication and authorization. Payment processing would be secured with HTTPS and appropriate security headers.

  3. Performance: Product catalog pages would use caching to improve response times. Database connection pooling would be optimized for the expected load.

  4. Monitoring: Application metrics would track order processing rates, payment success/failure, and API response times. Health checks would verify connectivity to payment gateways.

  5. Resilience: Circuit breakers would protect the application if payment processors or shipping API services experience downtime.

Example Deployment Configuration

yaml
# application-prod.yaml for an e-commerce application
spring:
datasource:
url: jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
hikari:
maximum-pool-size: 20
minimum-idle: 10
cache:
caffeine:
spec: maximumSize=1000,expireAfterWrite=60s
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://auth.example.com

server:
port: 8443
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-password: ${KEY_STORE_PASSWORD}
key-store-type: PKCS12
key-alias: ecommerce

payment:
gateway:
url: https://payment.example.com/api
api-key: ${PAYMENT_API_KEY}
timeout: 5s

shipping:
service:
url: https://shipping.example.com/api
api-key: ${SHIPPING_API_KEY}
timeout: 3s

management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
export:
prometheus:
enabled: true
endpoint:
health:
show-details: when-authorized
probes:
enabled: true

logging:
level:
com.example.ecommerce: INFO
org.springframework.web: WARN
org.hibernate: WARN
file:
name: /var/log/ecommerce/application.log

Production Deployment Checklist Summary

Here's a concise checklist to ensure your Spring application is ready for production:

  1. Configuration

    • Externalize all configuration
    • Set up environment-specific profiles
    • Store secrets in environment variables or secure vault
    • Review all default configuration values
  2. Security

    • Enable HTTPS
    • Configure security headers
    • Secure cookies and session management
    • Enable CSRF protection
    • Audit authentication and authorization logic
    • Remove default credentials and development backdoors
  3. Logging & Monitoring

    • Configure appropriate log levels
    • Set up log rotation
    • Implement health checks
    • Configure metrics collection
    • Set up alerting
  4. Performance

    • Enable caching where appropriate
    • Configure connection pooling
    • Optimize JVM settings
    • Review database queries and indexes
  5. Error Handling & Resilience

    • Implement global exception handling
    • Add circuit breakers for external services
    • Implement graceful degradation
    • Test failure scenarios
  6. Deployment

    • Create production Dockerfile or deployment scripts
    • Configure health check probes
    • Plan for zero-downtime deployments
    • Document deployment procedures

Additional Resources

To further enhance your Spring application for production, explore these resources:

Practice Exercise

Exercise: Security Audit

Perform a security audit on your Spring application by:

  1. Creating a list of all endpoints in your application
  2. Documenting what authentication and authorization requirements exist for each
  3. Verifying that sensitive data is properly encrypted at rest and in transit
  4. Testing your application with security scanning tools like OWASP ZAP
  5. Implementing improvements based on your findings

This exercise will help you identify potential security vulnerabilities before deploying to production.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)