How to Gracefully Shut Down a Spring Boot Application

This article explains why force‑killing a Spring Boot process is unsafe and walks through four practical ways—using Actuator, ApplicationContext.close(), PID‑based kill commands, and SpringApplication.exit()—to achieve a graceful shutdown while preserving in‑flight tasks and releasing resources.

Pan Zhi's Tech Notes
Pan Zhi's Tech Notes
Pan Zhi's Tech Notes
How to Gracefully Shut Down a Spring Boot Application

Graceful shutdown means that after a stop command the application stops accepting new requests, lets ongoing requests, scheduled jobs, and message‑consumer tasks finish, and then notifies dependent services before terminating, avoiding data loss or resource leaks such as unreleased Redis locks.

Using kill -9 <pid> terminates the JVM abruptly, which can corrupt in‑flight data and leave global resources unreleased.

Spring Boot’s official documentation suggests implementing specific interfaces to hook into startup and shutdown events. The key interfaces are: CommandLineRunner (or ApplicationRunner) – called after the application starts but before it begins handling traffic. DisposableBean or the @PreDestroy annotation – called just before the bean container is destroyed.

An example listener that prints messages on start and shutdown:

@Component
public class AppListener implements CommandLineRunner, DisposableBean {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("Application started, pre‑loading data");
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("Application shutting down, cleaning up data");
    }
}

Each SpringApplication registers a JVM shutdown hook that triggers the ApplicationContext to close, ensuring the above callbacks are invoked.

Method 1: Actuator shutdown endpoint

Add the dependency spring-boot-starter-actuator and enable the endpoint:

management.endpoint.shutdown.enabled=true
management.endpoints.web.exposure.include=shutdown

Then send a POST request to http://127.0.0.1:8080/actuator/shutdown to stop the service.

Method 2: Directly close the ApplicationContext

Obtain the ConfigurableApplicationContext from SpringApplication.run and call close() when needed. Example with a 10‑second delay:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
        context.close();
    }
}

A REST controller can expose a /shutdown endpoint that casts the injected ApplicationContext to ConfigurableApplicationContext and invokes close():

@RestController
public class ShutdownController implements ApplicationContextAware {
    private ApplicationContext context;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }
    @GetMapping("/shutdown")
    public void shutdownContext() {
        ((ConfigurableApplicationContext) context).close();
    }
}

Method 3: PID file and kill command

Configure Spring Boot to write its process ID to a file using ApplicationPidFileWriter:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.addListeners(new ApplicationPidFileWriter("/home/app/project1/app.pid"));
        app.run(args);
    }
}

Then safely stop the service with:

cat /home/app/project1/app.pid | xargs kill

Method 4: SpringApplication.exit()

Call SpringApplication.exit(context, () -> 0) to obtain an exit code and then terminate the JVM:

@SpringBootApplication
public class Application {
    public static void main(String[] args) throws InterruptedException {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        TimeUnit.SECONDS.sleep(5);
        int exitCode = SpringApplication.exit(context, () -> 0);
        System.exit(exitCode);
    }
}

Additional listeners

Implement ApplicationListener<ContextClosedEvent> to run custom logic (e.g., stop scheduled jobs) before the bean container is destroyed:

@Component
public class JobTaskListener implements ApplicationListener<ApplicationEvent> {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextClosedEvent) {
            System.out.println("Shutting down related scheduled tasks");
        }
    }
}

Or annotate a method with @PreDestroy for the same effect:

@Component
public class AppDestroyConfig {
    @PreDestroy
    public void preDestroy() {
        System.out.println("Application is shutting down…");
    }
}

These approaches collectively provide safe ways to stop a Spring Boot service without losing in‑flight work or leaving resources dangling.

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.

JavaGraceful ShutdownapplicationcontextActuatorspring-bootpre-destroy
Pan Zhi's Tech Notes
Written by

Pan Zhi's Tech Notes

Sharing frontline internet R&D technology, dedicated to premium original content.

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.