node-ops
Skill1 starsChanged 27 days agoMITView on GitHub
Production Node.js operations advisor. Always use this skill when deploying or operating Node.js applications, handling graceful shutdown, SIGTERM, clustering, memory leaks, health checks, readiness probes, uncaught exceptions, or process management. Use when the user asks "how do I handle SIGTERM in Node.js", "graceful shutdown in Express", "how do I add health checks or readiness probes", "my Node app crashes and restarts", "uncaught exception handler", "should I use cluster mode", or "Node.js kubernetes". Read this before advising on any Node.js production or deployment topic.
- Reads credentials
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 3 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 12
When a container or process manager sends SIGTERM, the app has a window to finish what it's doing before it's killed.
- Distinctive: line 48
Don't try to continue after an uncaught exception.
- Distinctive: line 118
Use structured logging (JSON): log aggregators (Datadog, CloudWatch, Splunk) can parse and query it
- Distinctive: line 121
Never log secrets, PII, or full request/response bodies by default
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Production Node.js Operations
A Node.js app that works in development often has silent operational problems in production: it crashes on uncaught errors, drops in-flight requests during deploys, leaks memory over time, or gives load balancers no way to know it's ready. These are all fixable, and they should be addressed before the first production deploy, not after the first incident.
Graceful shutdown
When a container or process manager sends SIGTERM, the app has a window to finish what it's doing before it's killed. If you don't handle it, the process exits immediately and drops every in-flight request.
const server = app.listen(3000);
process.on('SIGTERM', () => {
server.close(() => {
// connections drained — exit cleanly
process.exit(0);
});
// Safety valve: force exit if drain takes too long
setTimeout(() => process.exit(1), 10_000).unref();
});server.close() stops accepting new connections and waits for existing ones to finish. The timeout prevents the app from hanging indefinitely if a connection never closes (e.g., a long-polling client).
For database connections, message consumers, or other resources: close them in the SIGTERM handler too, after the HTTP server has stopped accepting work.
Handle uncaught exceptions and unhandled rejections
An uncaught exception or unhandled rejection leaves the process in an indeterminate state. The safest response is to log the error and exit: let the process manager restart you.
process.on('uncaughtException', (err) => {
logger.error('Uncaught exception', err);
process.exit(1);
});
process.on('unhandledRejection', (reason) => {
logger.error('Unhandled rejection', reason);
process.exit(1);
});Don't try to continue after an uncaught exception. The state of the event loop and any in-flight work is unknown. Restart fast and restart clean.
Liveness vs. readiness health checks
These are different endpoints with different semantics. Mixing them up causes deployment problems.
| Endpoint | Answers | Failure action |
GET /health/live | "Is the process alive?" | Restart the container |
GET /health/ready | "Can this instance serve traffic?" | Remove from load balancer rotation |
// Liveness: if this fails, something is fundamentally broken — restart
app.get('/health/live', (req, res) => res.sendStatus(200));
// Readiness: check dependencies before accepting traffic
app.get('/health/ready', async (req, res) => {
try {
await db.query('SELECT 1');
res.sendStatus(200);
} catch {
res.sendStatus(503);
}
});The readiness check should fail during startup (before the DB connection is established) and during graceful shutdown (after server.close() is called). This prevents traffic from reaching instances that can't handle it.
Clustering: when and how
Node.js runs on a single thread. A single instance won't saturate a multi-core machine. Options:
- Multiple container instances (preferred in Kubernetes/ECS): each container runs one Node process; the orchestrator handles distribution and restart. Simpler, easier to observe, and matches how most modern infra works.
- Node cluster module: spawns one worker per CPU core inside a single container. Works, but adds complexity: workers share no memory, IPC is manual, and a worker crash doesn't necessarily restart the master.
- PM2 cluster mode: wraps the cluster module with a process manager. Useful when you control the host but not the orchestrator.
In a containerized environment, prefer horizontal scaling over in-process clustering.
Memory management
Node.js V8 has a default heap limit (around 1.5GB on 64-bit). If you need more: NODE_OPTIONS='--max-old-space-size=4096'. But first investigate whether you actually have a leak.
Common leak sources:
- Event emitter listeners not removed:
emitter.on()without a correspondingemitter.off()or{ once: true } - Closures holding large objects: a callback capturing a large request object that never gets freed
- Unbounded caches: in-memory maps that grow forever with no eviction
- Timers not cleared:
setIntervalholding a reference to objects that should be GC'd
Diagnose with --inspect and a heap snapshot in Chrome DevTools, or clinic.js for production-safe profiling.
Environment configuration
Config belongs in environment variables, not in the repository or bundled into the process.
const config = {
port: parseInt(process.env.PORT ?? '3000', 10),
dbUrl: process.env.DATABASE_URL ?? throwMissing('DATABASE_URL'),
jwtSecret: process.env.JWT_SECRET ?? throwMissing('JWT_SECRET'),
};
function throwMissing(name: string): never {
throw new Error(`Required env var ${name} is not set`);
}Fail fast at startup if required config is missing: don't let the app start and then crash on the first request that needs the config.
Logging for production
- Use structured logging (JSON): log aggregators (Datadog, CloudWatch, Splunk) can parse and query it
- Include a correlation/request ID on every log line: essential for tracing a request across services
- Log at the right level:
errorfor actionable failures,warnfor degraded but functional,infofor significant events,debugfor local dev only - Never log secrets, PII, or full request/response bodies by default
What to watch for in code review
- No
SIGTERMhandler: process will drop in-flight requests on deploy process.exit()called without flushing logs or closing connections- A single
/healthendpoint used for both liveness and readiness uncaughtExceptionhandler that catches and continues instead of exiting- Large data structures cached in module scope with no eviction
- Required config accessed without validation at startup
In this repository25 other files it gives its agents.
- api-designSkill
- arch-reviewSkill
- db-reviewSkill
- debugSkill
- dotnet-asyncSkill
- dotnet-opsSkill
- dotnet-testingSkill
- dotnet-typesSkill
- go-concurrencySkill
- go-opsSkill
- go-testingSkill
- go-typesSkill
- incidentSkill
- node-asyncSkill
- perfSkill
- pr-prepSkill
- py-asyncSkill
- py-opsSkill
- py-testingSkill
- py-typesSkill
- refactorSkill
- securitySkill
- test-genSkill
- ts-testingSkill
- ts-typesSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

