Zero-downtime deployment of JavaScript services: how not to crash production

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Zero-downtime deployment: why is it critical for your JavaScript service?

\n\n

Imagine: Friday evening, you're deploying a new feature on Node.js — and within a minute, panic messages fly into the tech support chat: "The site is down!", "500 error!", "Data lost!". Sound familiar? If your JavaScript service stops responding during an update, you lose not only users but also money and reputation.

\n\n

Zero-downtime deployment is an approach where the code update happens unnoticed by the user. All requests are processed, connections are not broken, and the new code comes into operation smoothly. For Node.js, where everything is tied to asynchrony and events, this is especially important: one abrupt interruption can lead to memory leaks or unhandled promises.

\n\n

In this article, I will show you how to set up seamless deployment of JavaScript services in practice. We'll break down three working strategies — Rolling Update, Blue-Green Deployment, and Graceful Shutdown — with real code examples. Let's go!

\n\n

Strategy 1: Rolling Update — update without stopping

\n\n

Rolling update is the most popular technique in the Node.js world. The idea is simple: you have several running instances of the application, and you update them one by one. While one is restarting, the others continue to handle traffic.

\n\n

How it works in practice

\n

Let's say you have 4 Node.js processes running via PM2 (a popular process manager). The command pm2 reload app automatically performs a rolling restart: it kills one process at a time and starts the new code. But for this to work without errors, you need to properly configure graceful shutdown (more on that below).

\n\n

Example of PM2 configuration for zero-downtime

\n

Create a file ecosystem.config.js in the project root:

\n\n
module.exports = {\n  apps: [{\n    name: 'my-javascript-service',\n    script: './server.js',\n    instances: 4,                // 4 instances\n    exec_mode: 'cluster',       // cluster mode\n    wait_ready: true,           // wait for ready signal\n    listen_timeout: 5000,       // readiness timeout\n    kill_timeout: 5000,         // kill timeout\n    max_restarts: 10,\n    env: {\n      NODE_ENV: 'production'\n    }\n  }]\n};\n
\n\n

And in the server code (server.js), add signal handling:

\n\n
const http = require('http');\nconst app = require('./app'); // your Express/Koa application\n\nconst server = http.createServer(app);\nconst PORT = process.env.PORT || 3000;\n\nserver.listen(PORT, () => {\n  console.log(`Server started on port ${PORT}`);\n  // Send PM2 signal that the process is ready\n  if (process.send) {\n    process.send('ready');\n  }\n});\n\n// Graceful shutdown\nprocess.on('SIGINT', () => {\n  console.log('Received SIGINT, starting graceful shutdown...');\n  server.close(() => {\n    console.log('HTTP server closed');\n    process.exit(0);\n  });\n});\n\nprocess.on('SIGTERM', () => {\n  console.log('Received SIGTERM, shutting down...');\n  server.close(() => {\n    console.log('HTTP server closed');\n    process.exit(0);\n  });\n});\n
\n\n

Now when you run pm2 reload ecosystem.config.js, PM2 will stop and start instances one by one, waiting for the ready signal. Users won't notice the switch.

\n\n

Pros and cons of Rolling Update

\n
    \n
  • + Does not require additional servers or proxies.
  • \n
  • + Easy to configure via PM2 or Kubernetes.
  • \n
  • - If the new code contains a critical error, a "bad" instance may manage to process a few requests.
  • \n
  • - Requires careful state management (databases, cache).
  • \n
\n\n

Strategy 2: Blue-Green Deployment — two environments without risk

\n\n

Blogs

Book Recommendations