Shutdown libraries and shutdown tests solve different problems. A library adds signal handling and cleanup to your service. shutdown-check starts the real service and tests whether that behavior works from the outside.
What is shutdown-check for?
Use shutdown-check when you already have a signal handler—written by you, your framework, or a library—and want to verify the complete process lifecycle.
It checks:
- the production-like start command;
- readiness;
- a real active HTTP request;
SIGTERMdelivery;- response completion;
- optional traffic draining;
- process exit and exit code;
- final port closure.
It does not install a signal handler or close application resources for you.
Shutdown libraries
Libraries such as @godaddy/terminus, http-terminator, lightship, and
close-with-grace run inside the application. They may register signal
listeners, close HTTP servers, manage timeouts, and provide cleanup hooks.
Use a library when you need an implementation. Then test the configured result with shutdown-check. A correct library can still be used incorrectly: the wrong server object may be closed, cleanup may run in the wrong order, or the production command may prevent signals from reaching Node.js.
Framework shutdown hooks
Fastify, NestJS, and other frameworks provide shutdown APIs. These are also implementations, not end-to-end tests.
Framework tests often call a close method directly. A deployment sends a signal to a process. shutdown-check covers the signal listener, command, framework, open socket, and final exit together.
Unit tests
A unit test can call the shutdown function and assert that mocked dependencies were closed.
Strengths:
- fast;
- precise failure location;
- easy to cover error branches;
- suitable for database and queue cleanup logic.
Limits:
- usually no real child process;
- no operating-system signal;
- mocked HTTP connections;
- no proof that the production start command exits;
- no check for orphaned child processes.
Keep unit tests. Add a black-box test for the process behavior they cannot cover.
Shell scripts with kill and curl
A custom script can start the app, use curl, send kill -TERM, and inspect
the exit.
This approach can work, but reliable synchronization is difficult. A fixed sleep does not prove that a request was active when the signal arrived. The script must also handle concurrent requests, response bodies, timeouts, process groups, cleanup, exit codes, and CI reports.
shutdown-check packages those details into a repeatable config and stable diagnostic codes.
Staging and Kubernetes tests
A staging deployment is the only place to test cluster-level behavior such as
endpoint propagation, ingress timing, sidecars, and preStop hooks.
It is also slower and harder to reproduce. Requests may not be active during termination, and a successful rollout does not prove that no request was dropped.
Use shutdown-check before deployment for deterministic server behavior, then use staging tests for infrastructure behavior.
Comparison table
| Capability | shutdown-check | Shutdown library | Unit test | Shell script | Staging test |
|---|---|---|---|---|---|
| Adds shutdown behavior | No | Yes | No | No | No |
| Runs the real start command | Yes | N/A | Usually no | Yes | Yes |
Sends a real SIGTERM | Yes | Handles it | Usually no | Yes | Yes |
| Proves work is active before the signal | Yes | No | With mocks | Hard | Hard |
| Verifies response status and body | Yes | No | Yes | Possible | Possible |
| Checks readiness withdrawal | Optional | May implement | With mocks | Possible | Yes |
| Checks process exit and final port | Yes | No | No | Possible | Indirect |
| Checks databases and queues directly | No | May close them | Yes | Custom | Custom |
| Covers cluster routing and sidecars | No | No | No | No | Yes |
| Produces stable failure codes | Yes | No | Test names | Custom | Custom |
Which approach should I use?
For most services:
- Use framework features, a library, or a small handler to implement shutdown.
- Unit-test application-specific cleanup and error paths.
- Run shutdown-check locally and in CI for the process and HTTP lifecycle.
- Test Kubernetes, proxy, and load-balancer behavior in staging.
These layers complement each other. Replacing all of them with one test leaves important behavior unverified.
When shutdown-check is not enough
Add another test when correctness depends on:
- a database transaction after the HTTP response;
- queue acknowledgement;
- background jobs;
- WebSockets or HTTP/2;
- remote service coordination;
- container or cluster termination order.
The compatibility page describes the boundary in more detail.