# Zero-Downtime Deployment: Ship Updates Without Interrupting Users

· 5 min read
# Zero-Downtime Deployment: Ship Updates Without Interrupting Users

**Primary keywords:** zero downtime deployment, blue-green deployment, rolling deployment, zero downtime deploy, deploy without downtime
---
There's a particular kind of dread that comes with deploying to production during business hours. You run the deploy command and immediately worry: are users going to see errors? Is anyone in the middle of a checkout? Will the migration lock the orders table?
Zero-downtime deployment eliminates that dread. Done correctly, your users never experience a blip — requests that arrive during a deployment are served by either the old version or the new version, but never by a partially-updated system that crashes.
## What Causes Downtime During Deployments
To eliminate deployment downtime, you need to understand what causes it:
**1. Stopping the old server before the new one is ready**
If there's a gap between when the old server stops and the new one starts accepting traffic, requests fail. Classic deployments that restart a process in-place have this problem.
**2. Database migrations that lock tables**
An `ALTER TABLE` on a large table can hold a lock for minutes. During that time, any query touching the table fails.
**3. New code that references schema that doesn't exist yet**
If you deploy new application code that queries a new database column before the migration that creates that column runs, you get errors.
**4. Long-running requests that get cut off**
A user in the middle of a file upload or a checkout has their request terminated when the old server process is killed.
## Zero-Downtime Deployment Strategies
### Strategy 1: Rolling Deployment
In a rolling deployment, new instances are started and old instances are stopped incrementally, always maintaining minimum capacity.
```
Phase 1: [Old v1] [Old v1] [Old v1]   ← all old
Phase 2: [New v2] [Old v1] [Old v1]   ← 1 new, 2 old
Phase 3: [New v2] [New v2] [Old v1]   ← 2 new, 1 old
Phase 4: [New v2] [New v2] [New v2]   ← all new
```
Traffic is routed to healthy instances. Users hitting the system during the rollout might get either version, but they always get a working response.
Rolling deployments require that both the old and new versions can run simultaneously — they'll both be serving traffic during the rollout.
### Strategy 2: Blue-Green Deployment
Blue-green keeps two identical production environments, called "blue" and "green." At any time, one is live (serving traffic) and one is idle.
To deploy:
1. Deploy the new version to the idle environment
2. Run health checks and smoke tests against the idle environment
3. Switch traffic from the live environment to the idle environment (usually by changing a load balancer rule)
4. The previously-live environment becomes the new idle environment


```
Before: [Blue: v1 - LIVE] ←── traffic
[Green: v1 - idle]
Deploy:  [Blue: v1 - LIVE] ←── traffic
[Green: v2 - idle] ← deploy here and test
Switch: [Blue: v1 - idle]
[Green: v2 - LIVE] ←── traffic
fast cloud wordpress hosting
Rollback: [Blue: v1 - LIVE] ←── (switch back instantly)
[Green: v2 - idle]
```
fastify app deployment cloud
The key advantage: instant rollback. If something goes wrong, you switch traffic back in seconds.
### Strategy 3: Health-Check-Based Deployment (ApexWeave's Approach)
ApexWeave uses a health-check-based deployment model:
1. Build the new version
2. Start the new container alongside the old one
3. Wait for the new container to pass health checks
4. Switch traffic to the new container
5. Stop the old container
deploy fullstack app git
From the user's perspective, this is seamless. Requests in-flight when the switch happens complete against the old version. New requests go to the new version.
```bash
# ApexWeave handles this automatically on every deploy
apexweave deploy
```
You don't configure the rollout strategy — it's built into the platform.
## Writing Deploy-Safe Application Code
The platform can handle the infrastructure side of zero-downtime deployment, but your code needs to cooperate.
### 1. Graceful Shutdown
When the old server receives a shutdown signal, it should finish in-flight requests before stopping.
**Node.js:**
```javascript
const server = app.listen(process.env.PORT, () =>
console.log('Server started');
);
process.on('SIGTERM', () =>
console.log('Received SIGTERM, shutting down gracefully...');
server.close(() =>
console.log('Server closed');
process.exit(0);
);
// Force close after 30 seconds if server hasn't closed
setTimeout(() =>
console.error('Forcing shutdown');
process.exit(1);
, 30000);
);
```
**Python (Gunicorn):**
Gunicorn handles graceful shutdown automatically when it receives `SIGTERM`. Configure the timeout:
```bash
gunicorn app:app --timeout 30 --graceful-timeout 30
```
**Go:**
```go
srv := &http.ServerAddr: ":8080"
go func()
if err := srv.ListenAndServe(); err != http.ErrServerClosed
log.Fatalf("ListenAndServe: %v", err)
()
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil
log.Fatal("Server forced to shutdown:", err)
```
### 2. Health Check Endpoint
Implement a health check endpoint that returns 200 OK when the application is ready to serve traffic:
```javascript
// Node.js / Express
app.get('/health', async (req, res) =>
try
// Check database connectivity
await pool.query('SELECT 1');
res.json(
cheap cloud app hosting
status: 'healthy',
version: process.env.npm_package_version,
uptime: process.uptime()
);
catch (err)
res.status(503).json( status: 'unhealthy', error: err.message );
);
```
```python
# FastAPI
@app.get('/health')
laravel production hosting git
async def health_check():
try:
# Check DB
await db.execute('SELECT 1')
return 'status': 'healthy'
except Exception as e:
raise HTTPException(status_code=503, detail=str(e))
```
### 3. Zero-Downtime Database Migrations
This is the hardest part. The wrong migration can cause errors even with perfect deployment infrastructure.
**Rule: migrations must be backward-compatible with the old code.**
During a rolling or health-check deployment, old and new code run simultaneously. The database schema must work for both.
**Safe operations:**
- Adding a nullable column
- Adding a new table
- Adding an index (with `CONCURRENTLY` in PostgreSQL)
- Adding a column with a default value
**Unsafe operations (without extra steps):**
- Renaming a column
- Dropping a column the old code reads
- Adding a NOT NULL constraint to an existing column
- Changing a column's type
For unsafe operations, use the expand/contract pattern:
```sql
-- Step 1: Add new column (safe, old code ignores it)
ALTER TABLE users ADD COLUMN full_name VARCHAR(255);
-- Step 2: Deploy new code that writes to BOTH columns
-- Step 3: Backfill
UPDATE users SET full_name = name WHERE full_name IS NULL;
-- Step 4: Deploy code that only reads full_name
-- Step 5: Drop old column (safe, new code doesn't use it)
ALTER TABLE users DROP COLUMN name;
```
### 4. Backward-Compatible API Changes
When changing API responses, be careful about breaking existing clients (mobile apps, integrations) that are still on the old version:
```javascript
// Don't break existing clients by removing fields
// Bad: remove a field
return  name: user.fullName ;
// Good: add new field, keep old for transition period
return
fastify app deployment cloud
name: user.name,           // old field, deprecated
full_name: user.fullName   // new field
;
```
apexweave git deployment
## Implementing Pre-Deploy Checks
Before traffic switches to the new version, verify it's healthy:
```bash
#!/bin/bash
# deploy-with-checks.sh
echo "Deploying..."
apexweave deploy
echo "Waiting for health check..."
for i in 1..10; do
status=$(curl -s -o /dev/null -w "%http_code" https://myapp.apexweave.io/health)
if [ "$status" = "200" ]; then
echo "Health check passed"
exit 0
fi
echo "Health check attempt $i failed (status: $status)"
sleep 5
done
echo "Health check failed after 10 attempts"
exit 1
```
## Streaming Logs During Deploy
Watch what happens during a deployment:
```bash
# In one terminal: deploy
apexweave deploy
# In another terminal: watch logs
apexweave logs
```
You'll see the build output, startup messages, migration output, and any errors. This gives you confidence the deploy worked before moving on.
## Testing Your Zero-Downtime Setup
Test that your deployment is actually zero-downtime:
```bash
# Keep making requests during a deployment
while true; do
status=$(curl -s -o /dev/null -w "%http_code" https://myapp.apexweave.io/health)
apexweave wordpress hosting
echo "$(date): $status"
sleep 0.5
done &
# Deploy in another terminal
apexweave deploy
deploy web app production hosting
# Check that all requests returned 200
```
If any requests during the deploy returned 5xx errors, investigate your startup time and health check configuration.
## Try Zero-Downtime Deployments Free
ApexWeave handles zero-downtime deployment automatically on every `apexweave deploy`. No configuration required.
**Start your free 7-day trial** — no credit card needed.
```bash
npm install -g apexweave-cli
apexweave login
apexweave deploy
```
Ship fearlessly.