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.

Programmer1970
Programmer1970
Programmer1970
Spring Boot Graceful Shutdown Evolution: Principles and Best Practices

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 timeout

Shutdown 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                             | Excellent

Production‑grade practices

Enable graceful shutdown (2.3+)

server:
  shutdown: graceful
spring:
  lifecycle:
    timeout-per-shutdown-phase: 45s  # > max business request time

Kubernetes deployment with preStop hook

spec:
  containers:
  - name: app
    lifecycle:
      preStop:
        exec:
          command: ["sleep", "15"]  # give LB time to detach
    terminationGracePeriodSeconds: 60

Windows 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/shutdown

Monitoring & 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.
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavakubernetesSpring Bootyamlgraceful-shutdownspring-boot-2.3
Programmer1970
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.