Graceful Shutdown of Spring Boot Applications: Avoiding kill -9 and Using kill -15, Actuator, and Custom Shutdown Configurations

This article explains why using the forceful kill -9 command can cause data loss, demonstrates how to gracefully stop Spring Boot services with kill -15, the Actuator shutdown endpoint, and custom Tomcat shutdown configurations, and shows how to run cleanup tasks such as data backup during shutdown.

Top Architect
Top Architect
Top Architect
Graceful Shutdown of Spring Boot Applications: Avoiding kill -9 and Using kill -15, Actuator, and Custom Shutdown Configurations

In Linux, using kill -9 pid forcefully terminates a process, which can cause data inconsistency especially with MyISAM tables or distributed transactions.

For Spring Boot applications, a graceful shutdown is preferred. Sending kill -15 pid triggers an interrupt, allowing the application to finish ongoing requests before stopping.

Graceful shutdown with Spring Boot

Define a controller with a long‑running endpoint and test termination using kill -15. The logs show the interrupt exception and shutdown hook execution.

Using Actuator

Add spring-boot-starter-actuator and enable the /shutdown endpoint in application.yml. A POST request to /shutdown stops the service gracefully.

Custom shutdown configuration

Implement ElegantShutdownConfig that implements TomcatConnectorCustomizer and ApplicationListener<ContextClosedEvent> to pause the connector and wait for thread‑pool termination.

package com.ymy.config;

import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ElegantShutdownConfig implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
    private volatile Connector connector;
    private final int waitTime = 10;
    @Override
    public void customize(Connector connector) { this.connector = connector; }
    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        connector.pause();
        Executor executor = connector.getProtocolHandler().getExecutor();
        if (executor instanceof ThreadPoolExecutor) {
            ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
            threadPoolExecutor.shutdown();
            if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
                System.out.println("请尝试暴力关闭");
            }
        }
    }
}

Register this bean in the main application class and configure the servlet container to use it.

package com.ymy;

import com.ymy.config.ElegantShutdownConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class ShutdownServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ShutdownServerApplication.class, args);
    }
    @Bean
    public ElegantShutdownConfig elegantShutdownConfig() { return new ElegantShutdownConfig(); }
    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        tomcat.addConnectorCustomizers(elegantShutdownConfig());
        return tomcat;
    }
}

Data backup on shutdown

Use a @PreDestroy method in a @Configuration class to perform backup tasks before the JVM stops.

package com.ymy.config;

import org.springframework.context.annotation.Configuration;
import javax.annotation.PreDestroy;

@Configuration
public class DataBackupConfig {
    @PreDestroy
    public void backData() {
        System.out.println("正在备份数据。。。。。。。。。。。");
    }
}

These approaches allow Spring Boot services to stop without data loss, giving time for ongoing requests to finish and for custom cleanup logic to run.

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.

Javaprocess managementSpring BootGraceful ShutdownActuator
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.