Spring Boot in Production: A Pre-Deploy Checklist
The unglamorous settings that decide whether your Spring Boot service survives its first real traffic spike: externalized config, health probes, bounded timeouts, and graceful shutdown.
2 min read
The gap between "it works on my machine" and "it survives a Monday-morning traffic spike" is a handful of unglamorous settings. Before you promote a Spring Boot service to production, walk this checklist - every item on it has burned a team somewhere.
Externalize configuration and fail fast
Anything that differs between environments belongs outside the jar. Bind it to a typed, validated properties class so a missing value halts the boot instead of surfacing as a NullPointerException under load.
@Validated
@ConfigurationProperties(prefix = "billing")
public record BillingProperties(
@NotBlank String apiKey,
@NotNull Duration requestTimeout,
@Positive int maxRetries) {
}Pair this with spring.jpa.hibernate.ddl-auto=validate so the application refuses to start against a schema that has drifted from your migrations, and keep real secrets in a secret manager rather than in application.yml.
Give the platform real health signals
Load balancers and orchestrators only route correctly when you tell them the truth about readiness. Enable Actuator's probes and expose only what you need:
- Liveness answers "is the process wedged?" so the platform can restart it.
- Readiness answers "can it serve traffic right now?" so a warming-up or degraded instance is pulled from the pool.
- Expose
health,infoandprometheus, and keep every other endpoint off the public port.
Bound every outbound call
A production service is only as reliable as its slowest dependency. An HTTP client with no timeout will hang forever and quietly exhaust your thread pool.
@Bean
RestClient billingClient(RestClient.Builder builder) {
var factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(Duration.ofSeconds(2));
factory.setReadTimeout(Duration.ofSeconds(5));
return builder.requestFactory(factory).build();
}Size the database pool deliberately too: HikariCP's default of ten connections is a ceiling, not a target, so match it to what the database can actually serve.
Before you hit deploy
- Turn on graceful shutdown (
server.shutdown=graceful) so in-flight requests finish before the JVM exits. - Set explicit JVM heap limits that fit the container, or the OOM killer will choose for you.
- Confirm logs are structured and shipped off the box, not written to a local file nobody reads.
- Run one load test at the expected peak and watch the p99 latency, not the average.
None of this is exotic, but every item is a 3 a.m. page you get to skip. Treat the checklist as a gate, not a suggestion.

