Spring Boot Graceful Shutdown Evolution: Principles and Best Practices
The article explains why graceful shutdown is essential for high‑availability microservices, details the shortcomings of Spring Boot versions before 2.3, and walks through the native shutdown mechanism introduced in 2.3 together with configuration examples, code snippets, and production‑grade recommendations.
What is Graceful Shutdown?
Graceful shutdown means that during application termination the system:
Stops accepting new requests immediately.
Allows in‑flight requests to finish.
Safely releases resources such as database connections, thread pools, caches.
Prevents clients from receiving 5xx errors or connection resets.
If graceful shutdown is not implemented, rolling updates, scaling‑down, or restarts can trigger exceptions such as:
ERROR ... - Error creating bean with name 'orderController':
Singleton bean creation not allowed while singletons of this factory are in destruction
org.springframework.beans.factory.BeanCreationNotAllowedException: ...The exception indicates that the Spring container is being destroyed while a request still tries to obtain a controller bean, a direct symptom of an incorrect shutdown sequence.
Component hierarchy
JVM
└── Embedded Web Container (e.g., Tomcat)
└── Servlet Container (StandardContext)
└── DispatcherServlet (Spring MVC entry)
└── Spring ApplicationContext (IoC container)
└── All Spring Beans (@Controller, @Service, etc.)Key point: The Spring container runs inside the Web container; the shutdown order must be coordinated, otherwise a race condition occurs where the Web container still processes requests while the Spring container has already refused service.
Spring Boot < 2.3 – manual “pseudo‑graceful” implementation
Before version 2.3, Spring Boot did not provide built‑in graceful shutdown. The default behavior on receiving SIGTERM (e.g., kill -15) was:
Immediately close the Spring container (sets singletonsCurrentlyInDestruction = true).
The Web container continues to accept new connections and allocate threads.
If a request arrives, DispatcherServlet tries to fetch the controller bean via getBean("orderController").
Because the Spring container is marked as “destroying”, a BeanCreationNotAllowedException is thrown.
Root cause
Incorrect shutdown order: close Spring first, then Web container → creates a dangerous time window.
Developer workaround
Manually implement the following logic:
@Component
public class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
private volatile Connector connector;
@Override
public void customize(Connector connector) {
this.connector = connector;
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
connector.pause(); // stop receiving new requests
Executor executor = connector.getProtocolHandler().getExecutor();
if (executor instanceof ThreadPoolExecutor) {
try {
((ThreadPoolExecutor) executor).shutdown();
((ThreadPoolExecutor) executor).awaitTermination(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}Register the customizer with Tomcat:
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addConnectorCustomizers(gracefulShutdown());
return tomcat;
}Even with this workaround, new HTTP/1.1 keep‑alive connections can still be accepted during shutdown, so the solution is not perfect.
Spring Boot ≥ 2.3 – native graceful shutdown
Spring Boot 2.3 introduces a standardized graceful shutdown mechanism that can be enabled with two YAML lines:
server:
shutdown: graceful # enable graceful shutdown
spring:
lifecycle:
timeout-per-shutdown-phase: 30s # wait timeoutShutdown flow
Web container immediately pause() – stops accepting new connections.
Wait for in‑flight requests to finish, up to the configured timeout.
After no active requests remain, close the Spring container (destroy all beans).
Finally stop the Web container’s thread pool.
Underlying mechanism
SmartLifecycle controls shutdown phases
Spring Boot registers WebServerGracefulShutdownLifecycle, which implements SmartLifecycle and returns Integer.MAX_VALUE from getPhase(), ensuring it runs last (stops first). Its stop() method executes before ordinary bean destruction.
@Override
public int getPhase() {
return Integer.MAX_VALUE; // start last, stop first
}The stop() method runs before regular bean destruction.
WebServer interface standardization
Tomcat – pause() + wait for thread pool.
Jetty – stop acceptor thread.
Undertow – return 503 for new requests.
Reactor Netty – send GOAWAY (HTTP/2).
Deep integration with Spring lifecycle
Through LifecycleProcessor, Spring coordinates all SmartLifecycle beans so that the Web container finishes its graceful shutdown before ApplicationContext.close() is invoked.
Comparison before and after 2.3
Dimension | Spring Boot < 2.3 | Spring Boot ≥ 2.3
------------------------------|----------------------------------|-------------------------------
Built‑in support | ❌ Manual coding required | ✅ Out‑of‑the‑box
Shutdown start point | Spring container | Web container
New request handling | Continue accept → high risk | Immediate reject (pause/503) → safe
In‑flight request handling | May fail due to Spring closing | Wait until completion (max timeout)
Typical exception | BeanCreationNotAllowedException (frequent) | Rare (only on timeout)
Configuration complexity | High (custom listeners) | Low (YAML only)
Multi‑container support | Manual adaptation needed | Automatic for Tomcat/Jetty/Undertow/Netty
K8s friendliness | Poor | ExcellentProduction‑grade practices
Enable graceful shutdown (2.3+)
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 45s # > max business request timeKubernetes deployment with preStop hook
spec:
containers:
- name: app
lifecycle:
preStop:
exec:
command: ["sleep", "15"] # give LB time to detach
terminationGracePeriodSeconds: 60Windows service – use Actuator shutdown endpoint
management:
endpoint:
shutdown:
enabled: true
endpoints:
web:
exposure:
include: "shutdown"Trigger shutdown externally:
curl -X POST http://localhost:8080/actuator/shutdownMonitoring & alerts
Monitor 5xx error rate during shutdown.
If BeanCreationNotAllowedException still appears, check:
Whether the timeout is sufficient.
Load‑balancer removal delay.
Presence of long‑polling/WebSocket connections.
One‑line recommendation
If your Spring Boot version is ≥ 2.3, enable server.shutdown=graceful and combine it with infrastructure‑level traffic draining to dramatically improve stability during rolling updates and elastic scaling.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer1970
Formerly called 'Code to 35'. Add our main WeChat ID to access a wealth of shared resources (algorithms, interview prep, tech stacks: Java, Python, Go, big data). We mainly share serious development techniques, focusing on output-driven input. Occasionally we post life snippets and gossip. Our aim is to attract precise traffic and test advertising opportunities.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
