惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Azure Blog
Microsoft Azure Blog
WordPress大学
WordPress大学
小众软件
小众软件
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
V2EX
Hugging Face - Blog
Hugging Face - Blog
美团技术团队
博客园 - 三生石上(FineUI控件)
Last Week in AI
Last Week in AI
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - Franky
Microsoft Security Blog
Microsoft Security Blog
Y
Y Combinator Blog
A
About on SuperTechFans
The GitHub Blog
The GitHub Blog
U
Unit 42
H
Hackread – Cybersecurity News, Data Breaches, AI and More
云风的 BLOG
云风的 BLOG
IT之家
IT之家
MyScale Blog
MyScale Blog
V
Visual Studio Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
I
InfoQ
博客园 - 司徒正美

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Zero-Downtime Deployments for a React + Node App
Sohana Akbar · 2026-05-14 · via DEV Community

Sohana Akbar

Deploying a new version of your app without kicking users out or causing errors sounds like a dream, right? Let's make it real.

The Problem
You git push, run your build, restart the server... and for 5–30 seconds, your users see:

Cannot GET /

502 Bad Gateway

Or just a blank white screen of sadness

That's downtime. In 2025, it's not acceptable.

The Setup
We'll assume:

Frontend: React (built with Vite or CRA)

Backend: Node.js + Express

Server: Linux (Ubuntu)

Process manager: PM2

The Strategy
The goal: Old version keeps running until new version is ready.

Here's the plan:

Build the new React bundle

Prepare the new Node backend

Switch traffic atomically

Handle in-flight requests gracefully

Step 1: Build in a Staging Directory
Don't overwrite your live files directly.

bash

Clone fresh or pull latest

git pull origin main

Build frontend to a timestamped folder

TIMESTAMP=$(date +%s)
npm run build -- --dest /var/www/builds/$TIMESTAMP

Copy backend files to a versioned folder

cp -r backend /var/www/backends/$TIMESTAMP
Step 2: Use PM2 Clustering (Zero-Downtime Reload)
PM2 can reload your Node app one worker at a time.

bash

Start with max CPU cores

pm2 start backend/server.js -i max --name my-api

Later, to reload zero-downtime:

pm2 reload my-api
This keeps at least one process alive while others restart.

Step 3: Atomic Symlink Swap for React
Serve your React build via Nginx. Keep a current symlink.

bash

Initial

ln -sfn /var/www/builds/old /var/www/current

After new build ready

ln -sfn /var/www/builds/$TIMESTAMP /var/www/current
Nginx config:

nginx
location / {
root /var/www/current;
try_files $uri $uri/ /index.html;
}
The symlink change is instantaneous — no copying, no downtime.

Step 4: Graceful Shutdown in Node
Your Express app must handle SIGINT properly:

javascript
const server = app.listen(PORT);

process.on('SIGINT', () => {
console.log('Closing gracefully...');
server.close(() => {
console.log('All connections closed');
process.exit(0);
});

// Force close after 10s
setTimeout(() => process.exit(1), 10000);
});
Now PM2's reload won't kill active requests.

Step 5: Put It All Together (Deploy Script)
bash

!/bin/bash

set -e

TIMESTAMP=$(date +%s)

Pull code

git pull origin main

Build backend

cp -r backend /var/www/backends/$TIMESTAMP
pm2 reload my-api --update-env

Build frontend

npm run build
cp -r dist /var/www/builds/$TIMESTAMP

Atomic swap

ln -sfn /var/www/builds/$TIMESTAMP /var/www/current

Clean old builds (keep last 5)

ls -1 /var/www/builds | head -n -5 | xargs -I {} rm -rf /var/www/builds/{}
ls -1 /var/www/backends | head -n -5 | xargs -I {} rm -rf /var/www/backends/{}
Step 6: Handle Database Migrations
Migrations are tricky. Two safe approaches:

Approach A (safer): Run migrations before deploying new code, ensuring backward compatibility.

Approach B: Run migrations during a low-traffic window with a lock file.

javascript
// Before starting new version
await runMigrations();
Step 7: Session & Auth Gotcha
If you store sessions in memory, users will be logged out after reload.

Fix: Use Redis for sessions.

javascript
const session = require('express-session');
const RedisStore = require('connect-redis')(session);

app.use(session({
store: new RedisStore({ host: 'localhost' }),
secret: 'keyboard cat',
resave: false
}));
Testing Zero-Downtime Locally
Before shipping to prod:

bash

Terminal 1: run app

pm2 start server.js -i 2
pm2 logs

Terminal 2: send constant traffic

while true; do curl http://localhost:3000; sleep 0.1; done

Terminal 3: reload

pm2 reload server
You should see zero errors and zero connection resets.

Common Pitfalls
Problem Solution
WebSocket connections drop Reconnect on client, or use Socket.IO with Redis adapter
File uploads in progress Increase graceful shutdown timeout
React app cache issues Use chunk hashing (Vite/CRA does this automatically)
Environment variables not updated Use pm2 reload --update-env
The Result
✅ Users stay logged in
✅ No 502 errors
✅ Deploy at 3 PM on a Tuesday
✅ Sleep better at night

Final Thoughts
Zero-downtime isn't magic — it's just careful swapping of assets and graceful process handling.

Start with the symlink trick for your React build + PM2 reload for Node. That covers 90% of apps.

For larger systems, add a load balancer (like HAProxy or Nginx upstreams) and do blue-green deployments. But that's a story for another day.

Have you tried this approach? What's your zero-downtime setup like? Drop a comment below!