The npm SIGTERM error occurs when npm receives a termination signal, typically from container orchestrators, process managers, or system shutdowns, and fails to pass it gracefully to the Node.js child process.
SIGTERM (signal termination) is a Unix signal that tells a process to terminate gracefully. When npm receives a SIGTERM signal—typically from a container orchestrator, process manager, or deployment platform—it should pass that signal to its child Node.js process. However, npm doesn't reliably forward this signal, causing npm to immediately kill the Node.js process instead of allowing graceful shutdown. The root cause is architectural: when you run npm run <script>, npm becomes the main process, and your Node.js application becomes a child process. Unlike proper process managers, npm doesn't intercept signals intelligently. When Linux sends SIGTERM to the npm process group, npm kills all children without forwarding the signal, preventing your application from running cleanup routines. This issue is particularly prevalent in Docker containers, Kubernetes, Heroku, and CI/CD platforms where signal routing is critical for graceful shutdowns.
The simplest and most effective fix is to run Node.js directly instead of through npm:
In your Dockerfile, change:
# ❌ Before (bad - npm intercepts signals)
CMD npm start
# ✅ After (good - Node.js is PID 1)
CMD ["node", "./dist/index.js"]For Heroku or cloud platforms, update your Procfile:
# ❌ Before
web: npm start
# ✅ After
web: node ./dist/index.jsThis ensures your Node.js process becomes PID 1 and receives signals directly.
Ensure you're using the exec form (JSON array format) instead of shell form:
# ❌ Shell form (bad - signals lost in shell)
CMD "node ./src/index.js"
# ✅ Exec form (good - signals forwarded directly)
CMD ["node", "./src/index.js"]The exec form tells Docker to execute the command directly without wrapping it in /bin/sh -c, preserving signal routing.
Add signal handlers at the top of your main application file:
// index.js - add at the very top
process.on('SIGTERM', () => {
console.log('SIGTERM received, starting graceful shutdown...');
server.close(() => {
console.log('HTTP server closed');
process.exit(0);
});
});
process.on('SIGINT', () => {
console.log('SIGINT received, shutting down...');
process.exit(0);
});
const express = require('express');
const app = express();
const server = app.listen(3000, () => {
console.log('Server running on port 3000');
});Register handlers before calling listen() so they're available when shutdown signals arrive.
Use an init system to ensure signals are properly forwarded:
Option A: Use Docker --init flag:
docker run --init -p 3000:3000 my-node-appOption B: Use Tini in your Dockerfile:
FROM node:20-alpine
RUN apk add --no-cache tini
WORKDIR /app
COPY . .
RUN npm ci --omit=dev
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "./dist/index.js"]Option C: In docker-compose.yml:
services:
app:
build: .
init: true
ports:
- "3000:3000"In some cloud platforms, SIGTERM is sent when your application exceeds its memory limit:
# Locally, test with memory constraints:
node --max-old-space-size=256 ./dist/index.jsOptimize memory usage:
// ✅ Use streaming instead of loading entire datasets
app.get('/data', (req, res) => {
fs.createReadStream('large-file.json').pipe(res);
});
// ❌ Avoid: loading entire file into memory
app.get('/data', (req, res) => {
const data = fs.readFileSync('large-file.json');
res.send(data);
});If your platform consistently sends SIGTERM shortly after startup, increase your memory allocation.
Process Management Context: SIGTERM is part of the Unix process management protocol. When you send docker stop, Kubernetes performs a graceful shutdown, or a process manager needs to terminate an application, it sends SIGTERM (signal 15) first, giving the process ~10 seconds to shut down gracefully. If the process doesn't exit, it sends SIGKILL (signal 9), which cannot be caught.
PID 1 and Signal Handling: In Unix/Linux, PID 1 is the init process with special signal handling rules. Node.js doesn't naturally handle being PID 1 well, which is why it won't respond to SIGINT or SIGTERM when it's the container's main process. This is solved by using an init system (Tini, dumb-init) or not making npm/Node PID 1.
Kubernetes-Specific: Kubernetes sends SIGTERM with a default grace period of 30 seconds. Ensure your signal handler exits within this window. Also check readiness and liveness probes—a failed readiness probe doesn't stop the pod; only deleting it or a SIGTERM will.
npm ERR! code ENOAUDIT npm ERR! Audit endpoint not supported
How to fix "npm ERR! code ENOAUDIT - Audit endpoint not supported"
npm ERR! code EBADDEVENGINES npm ERR! devEngines.runtime incompatible with current node version
How to fix "npm ERR! code EBADDEVENGINES - devEngines.runtime incompatible with current node version"
npm ERR! code ETOOMANYARGS npm ERR! Too many arguments
How to fix "npm ERR! code ETOOMANYARGS - Too many arguments"
npm ERR! code EINVALIDTAGNAME npm ERR! Invalid tag name: tag names cannot contain spaces
How to fix "npm ERR! code EINVALIDTAGNAME - tag names cannot contain spaces"
npm ERR! code E400 npm ERR! 400 Bad Request
How to fix "npm ERR! code E400 - 400 Bad Request" error