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.
// 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.
// 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:
java -jar myapp.jar --spring.profiles.active=prod
Use Environment Variables for Sensitive Information
Never commit sensitive information (passwords, API keys) to version control.
# 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:
# 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:
@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:
# 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:
@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:
# 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:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# 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:
@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:
@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:
@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:
# 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:
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:
@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:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
@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
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:
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:
-
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.
-
Security: All customer data endpoints would require authentication and authorization. Payment processing would be secured with HTTPS and appropriate security headers.
-
Performance: Product catalog pages would use caching to improve response times. Database connection pooling would be optimized for the expected load.
-
Monitoring: Application metrics would track order processing rates, payment success/failure, and API response times. Health checks would verify connectivity to payment gateways.
-
Resilience: Circuit breakers would protect the application if payment processors or shipping API services experience downtime.
Example Deployment Configuration
# 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:
-
Configuration
- Externalize all configuration
- Set up environment-specific profiles
- Store secrets in environment variables or secure vault
- Review all default configuration values
-
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
-
Logging & Monitoring
- Configure appropriate log levels
- Set up log rotation
- Implement health checks
- Configure metrics collection
- Set up alerting
-
Performance
- Enable caching where appropriate
- Configure connection pooling
- Optimize JVM settings
- Review database queries and indexes
-
Error Handling & Resilience
- Implement global exception handling
- Add circuit breakers for external services
- Implement graceful degradation
- Test failure scenarios
-
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:
- Spring Boot Production-Ready Features
- Spring Security Documentation
- Spring Boot Actuator
- 12-Factor App Methodology
- Spring Cloud for Microservices
Practice Exercise
Exercise: Security Audit
Perform a security audit on your Spring application by:
- Creating a list of all endpoints in your application
- Documenting what authentication and authorization requirements exist for each
- Verifying that sensitive data is properly encrypted at rest and in transit
- Testing your application with security scanning tools like OWASP ZAP
- 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! :)