Shutdown Check

Search documentation

Find a page or section

Answers about signals, active requests, frameworks, containers, and test behavior.

These answers cover the most common Node.js graceful shutdown questions. Each answer links to a guide or reference page when you need working code or deeper troubleshooting.

What is graceful shutdown in Node.js?

Graceful shutdown is the process of stopping a service without cutting off work that already started. After SIGTERM, the service stops taking new work, finishes active requests, closes shared resources, and exits before the platform deadline.

Node.js does not build this application-specific sequence for you. Add a signal handler or use a shutdown library, then verify the behavior. The Node.js guide includes complete node:http, Express, and Fastify examples.

Does shutdown-check make my app shut down gracefully?

No. shutdown-check is a test tool, not a shutdown-handler library. It does not change your service or run inside it.

The tool starts the configured command, opens a real request, sends SIGTERM, and checks the response and process. You still need to implement the handler, close resources, and choose a drain strategy.

Why does Node.js exit immediately on SIGTERM?

Without an effective listener, the signal uses its default termination behavior. Any open HTTP connection can be cut off.

Install a listener on the process that actually runs the server:

process.on("SIGTERM", () => {
  server.close((error) => {
    if (error) process.exitCode = 1;
  });
});

If a shell or package-manager wrapper starts Node.js, the signal may stop at the wrapper. Prefer a direct command such as node dist/server.js.

Why does server.close() not finish?

server.close() waits for active connections. It can appear stuck when:

  • an HTTP response never ends;
  • an upstream or database operation hangs;
  • a client keeps a connection active;
  • the application closes a dependency that the request still needs;
  • another resource keeps Node.js alive after the server closes.

Log the callback, active handlers, and every cleanup step. Add timeouts to external operations. SC200 covers a request that stays open; SC300 covers a process that stays alive after requests finish.

Why does my test say the work was never in flight?

The workload ended before the signal, or its start barrier never passed. A shutdown test cannot prove draining when no work is active.

With response-headers, flush headers and keep the body open. With probe, make a separate route change from inactive to active while the operation runs. See in-flight work and start barriers.

Does it work with Express, Fastify or NestJS?

Yes. shutdown-check does not depend on a framework. It starts a command and uses local HTTP.

  • Express uses the Node.js server returned by app.listen().
  • Fastify can close through fastify.close().
  • NestJS needs its shutdown hooks configured and awaited.

The service must expose readiness and workload routes and respond to SIGTERM. Framework-specific implementation is covered in the graceful shutdown guide.

Does it test databases, queues or WebSockets?

Not directly. shutdown-check observes the service process and HTTP/1 responses. It cannot see internal database or queue state, and it does not open WebSocket or HTTP/2 sessions.

Use bodyIncludes when the response can prove that important work completed. Add application-specific tests for database transactions, queue acknowledgement, background tasks, WebSockets, and work that continues after the response.

Does it work on Windows?

Not natively. The tool requires POSIX signals and process groups, so it supports macOS and Linux.

On a Windows workstation, run it in WSL, a Linux container, or Linux CI. The service and test must run in the same supported environment with access to the local port.

Can I run it in Docker or Kubernetes?

Yes, with limits. It can run inside a Linux container that has Node.js 22 or later. It tests the service process and local HTTP behavior inside that container.

It does not simulate Kubernetes endpoint removal, preStop, sidecars, ingress, or the cluster grace period. Use the Kubernetes guide to map those settings and test the remaining behavior in staging.

How is it different from a shutdown library like terminus?

A library such as terminus implements shutdown behavior inside the service. shutdown-check tests the behavior from outside the service.

You can use both: a library to register handlers and close resources, then shutdown-check to prove the real start command, signal path, active request, exit, and port work together. See the comparison page for unit tests, scripts, and staging tests as well.

Does shutdown-check send SIGINT?

No. Version 1.0.1 supports SIGTERM only. A config with another signal is rejected. This matches the graceful termination path used by container runtimes and most process managers.

Can it test several requests at once?

Yes. Set workload.concurrent from 1 to 20 and use the response-headers start barrier. Every request must be active before the signal and must finish with the expected response.

A probe barrier supports one request because a shared probe cannot identify which of several operations started.

Why does the CLI exit with code 2 and no SC code?

The test did not run. Exit code 2 is used for bad command-line arguments, missing or invalid JSON, and config validation errors. The stderr message starts with shutdown-check:.

Fix that message first. SC codes are produced only after a valid test starts. See troubleshooting.

How should I choose the shutdown deadline?

The deadline must cover the slowest valid request and cleanup after SIGTERM, with a margin. It must also stay below the platform's forced-kill deadline.

For Kubernetes, subtract preStop time from terminationGracePeriodSeconds. Do not use a longer deadline to hide work that can hang forever; add operation and cleanup timeouts.

What should I run in CI?

Build the application, then run:

npx shutdown-check test --json --junit shutdown-result.xml

Exit code 1 fails the job for a shutdown problem. The JUnit report can be published with other test results. Use a dedicated port and isolated test data.