Deploying a Node service
Ship a service that reports health, shuts down gracefully and separates build from runtime.
- Keep build tools out of the runtime dependency set.
- Expose a /health endpoint for readiness checks.
- Handle SIGTERM and SIGINT to close the server cleanly.
- Drive differences between environments with variables, not code changes.
In plain English
Deploying a server means running it somewhere other than your laptop, so it must behave well under a process manager. Build dependencies such as a compiler or test runner are needed only while building, while runtime dependencies are needed to run the service. A health endpoint is a small route that returns ok so a load balancer can tell whether the service is alive. Graceful shutdown means stopping new requests and finishing in-flight ones when the process receives a signal such as SIGTERM. Structured logs and environment variables keep the deployed service configurable and observable.
Step by step
- 1Install build tools as dev dependencies so production installs skip them.
- 2Add a /health route that reports the service state.
- 3Listen for SIGTERM and SIGINT and close the server.
- 4Set a timeout so a stuck server still exits eventually.
import { createServer } from 'node:http'
const port = Number(process.env.PORT ?? 3000)
const server = createServer((request, response) => {
if (request.url === '/health') {
response.writeHead(200, { 'content-type': 'application/json' })
return response.end(JSON.stringify({ status: 'ok' }))
}
response.writeHead(404)
response.end()
})
server.listen(port, () => {
console.log('Listening on ' + port)
})
function shutdown(signal: string) {
console.log('Received ' + signal + ', closing server')
server.close(() => {
console.log('Server closed')
process.exit(0)
})
setTimeout(() => {
console.error('Forced shutdown after timeout')
process.exit(1)
}, 10_000).unref()
}
process.on('SIGTERM', () => shutdown('SIGTERM'))
process.on('SIGINT', () => shutdown('SIGINT'))Common mistakes
- Shipping test runners and compilers to production bloats the image and widens the attack surface.
- Ignoring SIGTERM makes a deploy kill the process mid-request and lose work.
- Returning ok from /health even when the database is down hides a real outage.
- Hard-coding ports and URLs means the service cannot move between environments.
Close the server on a signal
Add a SIGTERM handler that calls server.close and exits with code 0 when the close callback fires.
const server = createServer((request, response) => {
response.end('ok')
})
server.listen(3000)What is the purpose of a /health endpoint?
Make it yours.
Keep a private note for this lesson, then turn the key ideas into flashcards you can revisit.
Lock in what you just learned.
Mark this lesson complete to save your place and move one step closer to the certificate.