Zero-downtime Deployment of TypeScript Services: A Step-by-Step Guide

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

How to Update a Service Without Waking Up Users?



Imagine: Friday evening, you decide to quickly push a bugfix to production. You hit deploy — and your messenger, online cinema, or CRM goes down for 30 seconds. Thousands of users at that moment see a 502 Bad Gateway. And if it's a payment gateway? Count the losses.



Zero-downtime deployment is not a luxury, but a standard for modern TypeScript services. In this article, we'll break down real techniques used in production: from graceful shutdown to blue-green deployment and rolling updates. You'll get ready-to-use TypeScript patterns.



Why is a Regular TypeScript Application Deployment Dangerous?



When you run npm run build && node dist/index.js, several problems occur:



  • Connection breaks — the old process is killed while new requests are still coming in.
  • In-memory data loss — unsaved sessions, cache, queues.
  • Cold start — the new process hasn't warmed up yet but is already receiving traffic.


In TypeScript (Node.js), this is exacerbated by asynchronicity: you don't know which promises are still executing. But there is a solution.



Technique #1: Graceful Shutdown



The foundation of zero-downtime is proper handling of OS signals. When an orchestrator (Kubernetes, PM2) decides to stop your service, it sends SIGTERM. Your code should stop accepting new requests, wait for current ones to finish, and only then exit.



Implementation in TypeScript with Express



import express, { Request, Response } from 'express';import http from 'http';

const app = express();let server: http.Server;

app.get('/health', (req: Request, res: Response) => { res.status(200).json({ status: 'ok' });});

// Signal from Docker/K8sprocess.on('SIGTERM', async () => { console.log('SIGTERM received, starting graceful shutdown...'); // 1. Stop listening for new connections server.close(() => { console.log('HTTP server closed'); // 2. Close connections to DB, Redis, etc. // await db.disconnect(); process.exit(0); });

// 3. Give current requests time (30 sec timeout) setTimeout(() => { console.error('Forced shutdown due to timeout'); process.exit(1); }, 30000);});

const PORT = process.env.PORT || 3000;server = app.listen(PORT, () => { console.log(`Service running on port ${PORT}`);});


Important: in TypeScript, use http.Server from the http module, not the Express type. The server.close() method correctly handles keep-alive connections.



Technique #2: Rolling Update



If you have multiple service replicas (3-5 instances running), a rolling update updates them one by one. While one is restarting, the others handle traffic.



Example for Kubernetes Deployment



# deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata:  name: my-typescript-servicespec:  replicas: 3  strategy:    type: RollingUpdate    rollingUpdate:      maxUnavailable: 1      # how many pods can be unavailable      maxSurge: 1            # how many new pods can be created above the plan  template:    spec:      containers:      - name: app        image: myregistry/service:v2        ports:        - containerPort: 3000        livenessProbe:          httpGet:            path: /health            port: 3000          initialDelaySeconds: 5          periodSeconds: 10        readinessProbe:          httpGet:            path: /ready            port: 3000          initialDelaySeconds: 3          periodSeconds: 5


Add a /ready endpoint to your TypeScript application that returns 200 only when the service

Blogs

Book Recommendations