Stopping n8n for an upgrade interrupts active executions and makes webhooks return errors.
Stopping n8n for an upgrade interrupts active executions and makes webhooks return errors. A blue-green deployment runs the new version alongside the old one, verifies it works, then switches traffic. Both versions share the same database, so n8n's migration system handles schema changes during startup of the new version.
Real-world example: You upgrade from 1.93.0 to 1.94.1 without any webhook downtime.
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n_user -d n8n_db"]
interval: 10s
timeout: 5s
retries: 5
n8n-blue:
image: n8nio/n8n:1.93.0
restart: unless-stopped
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_DATABASE: n8n_db
DB_POSTGRESDB_USER: n8n_user
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:5678/healthz || exit 1"]
interval: 30s
timeout: 10s
retries: 3
n8n-green:
image: n8nio/n8n:1.94.1
restart: unless-stopped
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_DATABASE: n8n_db
DB_POSTGRESDB_USER: n8n_user
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:5678/healthz || exit 1"]
interval: 30s
timeout: 10s
retries: 3
volumes:
postgres_data:
```text
```bash title="blue-green-switch.sh"
#!/usr/bin/env bash
set -euo pipefail
# Step 1: Start the green (new) instance
docker compose -f docker-compose.blue-green.yml up -d n8n-green
# Step 2: Wait for green to pass health checks
echo "Waiting for green instance to become healthy..."
timeout 180 bash -c '
until docker inspect --format="{{.State.Health.Status}}" n8n-green-1 2>/dev/null | grep -q healthy; do
sleep 5
done
'
echo "Green instance is healthy."
# Step 3: Update Caddy to point to green
# (swap upstream in Caddyfile from n8n-blue:5678 to n8n-green:5678)
sed -i 's/n8n-blue:5678/n8n-green:5678/' Caddyfile
docker exec caddy-1 caddy reload --config /etc/caddy/Caddyfile
# Step 4: Verify traffic is flowing to green
sleep 10
curl -sf https://n8n.example.com/healthz > /dev/null && echo "Traffic switched to green."
# Step 5: Stop the blue (old) instance
docker compose -f docker-compose.blue-green.yml stop n8n-blue
echo "Blue-green deployment complete."
```text
> **Warning: Database Migrations**
>
> n8n runs database migrations on startup. Once the green instance starts and migrates the schema, the blue instance may not be compatible with the new schema. Keep the cutover window short and always have a database backup from before the migration.
This approach eliminates the 30-120 second downtime window that a simple `docker compose pull && docker compose up -d` creates.
**Related:** [Set a Unique Encryption Key and Back It Up](../security-best-practices/01-set-a-unique-encryption-key-and-back-it-up.md) | [Configure Payload Size and Binary Data Mode for Large Files](../performance-and-large-files/01-configure-payload-size-and-binary-data-mode-for-large-files.md)
I build production n8n and Cloudflare automation for teams — the same engineering behind HarperFlow. Fixed-price, escrow-protected, US-based.