Backend Development 11 min read

Graceful Shutdown of Spring Boot Applications Using Linux Signals and Actuator

The article explains how to achieve graceful shutdown of Spring Boot services by handling Linux signals, registering JVM shutdown hooks, using Spring's ContextClosedEvent and Actuator endpoint, and provides practical code examples for safely terminating thread pools, sockets, and other resources.

Architecture Digest
Architecture Digest
Architecture Digest
Graceful Shutdown of Spring Boot Applications Using Linux Signals and Actuator

What Is Graceful Shutdown

Graceful shutdown means that before an application process is terminated it notifies the running code to release resources such as thread pools, sockets, temporary files, and both heap and off‑heap memory, and to deregister from service registries.

Thread pool: shutdown() (no new tasks) or shutdownNow() (interrupt running tasks via Thread.interrupt ).

Socket connections (e.g., Netty, MQ).

Notify service registries (e.g., Eureka) to go offline quickly.

Clean temporary files (e.g., POI generated files).

Release various heap and off‑heap memory.

Forcefully killing a process can cause data loss, unrecoverable state, or data inconsistency in distributed environments.

kill Command

kill -9 pid simulates a hard crash (SIGKILL), while kill -15 pid (SIGTERM) asks the application to shut down gracefully, allowing it to finish pending work.

# View JVM process IDs
jps
# List all signal names
kill -l
# Linux signal constants
#   INT   SIGINT     2   Ctrl+C interrupt
#   TERM  SIGTERM    5   Software termination signal
#   KILL  SIGKILL    9   Forceful termination
# Send signals
kill -9 pid   # force kill
kill -15 pid  # graceful termination
Think: How does the JVM handle Linux signals?

The JVM registers a custom SignalHandler at startup; when a signal arrives the handler invokes Shutdown.exit to start the shutdown sequence.

public interface SignalHandler {
    SignalHandler SIG_DFL = new NativeSignalHandler(0L);
    SignalHandler SIG_IGN = new NativeSignalHandler(1L);
    void handle(Signal var1);
}

class Terminator {
    private static SignalHandler handler = null;
    static void setup() {
        if (handler == null) {
            SignalHandler var0 = new SignalHandler() {
                public void handle(Signal var1) {
                    Shutdown.exit(var1.getNumber() + 128);
                }
            };
            handler = var0;
            try { Signal.handle(new Signal("INT"), var0); } catch (IllegalArgumentException e) {}
            try { Signal.handle(new Signal("TERM"), var0); } catch (IllegalArgumentException e) {}
        }
    }
}

Runtime.addShutdownHook

Calling Runtime.getRuntime().addShutdownHook(Thread hook) registers a hook that the JVM executes when it begins shutting down.

public class Runtime {
    public void addShutdownHook(Thread hook) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) sm.checkPermission(new RuntimePermission("shutdownHooks"));
        ApplicationShutdownHooks.add(hook);
    }
}

class ApplicationShutdownHooks {
    private static IdentityHashMap
hooks;
    static synchronized void add(Thread hook) {
        if (hooks == null) throw new IllegalStateException("Shutdown in progress");
        if (hook.isAlive()) throw new IllegalArgumentException("Hook already running");
        if (hooks.containsKey(hook)) throw new IllegalArgumentException("Hook previously registered");
        hooks.put(hook, hook);
    }
}

Spring 3.2.12 Context Closed Event

Spring publishes a ContextClosedEvent when the application context is closed. The LifecycleProcessor.onClose method then stops all beans that implement Lifecycle .

public abstract class AbstractApplicationContext extends DefaultResourceLoader {
    public void registerShutdownHook() {
        if (this.shutdownHook == null) {
            this.shutdownHook = new Thread() {
                @Override public void run() { doClose(); }
            };
            Runtime.getRuntime().addShutdownHook(this.shutdownHook);
        }
    }
    protected void doClose() {
        // publish ContextClosedEvent
        publishEvent(new ContextClosedEvent(this));
        // stop Lifecycle beans
        getLifecycleProcessor().onClose();
        // destroy beans, close BeanFactory, etc.
    }
}

Spring Boot Actuator Shutdown Endpoint

Spring Boot’s spring-boot-starter-actuator module provides a REST endpoint ( /shutdown ) that triggers a graceful shutdown by invoking AbstractApplicationContext.close() .

# Enable the shutdown endpoint
endpoints.shutdown.enabled=true
# Disable password protection (for internal use only)
endpoints.shutdown.sensitive=false
management.context-path=/manage
management.port=8088
management.address=127.0.0.1

When the endpoint is called (e.g., curl -X POST http://127.0.0.1:8088/shutdown ) the actuator creates a new thread that eventually calls context.close() , which runs all registered shutdown hooks and releases resources.

@ConfigurationProperties(prefix = "endpoints.shutdown")
public class ShutdownEndpoint extends AbstractEndpoint
> {
    private ConfigurableApplicationContext context;
    public Map
invoke() {
        if (this.context == null) return NO_CONTEXT_MESSAGE;
        Thread thread = new Thread(() -> this.context.close());
        thread.setContextClassLoader(this.getClass().getClassLoader());
        thread.start();
        return SHUTDOWN_MESSAGE;
    }
}

In production environments the shutdown URL should be secured with Spring Security or network restrictions.

JVMSpring BootGraceful Shutdownshutdown-hookActuatorLinux Signals
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

login 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.