Zero-Downtime Deployments
Choose a deployment strategy that fits your uptime requirements. Compare standard, rolling, and blue/green approaches with config examples.
Every deployment has a moment where old code stops and new code starts. How you handle that moment determines whether your users notice. JuhJuh supports three deployment strategies, each with a different trade-off between simplicity, cost, and downtime.
Strategy comparison¶
| Standard | Rolling | Blue/Green | |
|---|---|---|---|
| Downtime | Brief (5-30 seconds) | Minimal (1-2 seconds) | None |
| Rollback speed | Minutes (redeploy previous version) | Minutes | Seconds (traffic switch) |
| VMs required | 1 | 1 | 3 (1 load balancer + 2 application) |
| Extra cost | None | None | 2 additional VMs |
| Complexity | Low | Low | Medium |
| Best for | Internal tools, staging, dev | Production with tolerance for brief blips | Production with strict uptime requirements |
| WebSocket handling | Connections drop on restart | Connections drain before restart | Connections drain before switch |
| Health check | Post-deploy only | Per-container during rollout | Pre-switch verification |
Standard deployment¶
The default strategy. JuhJuh pulls the new container image, stops the running service, and starts the new version. One command, one restart, done.
How it works:
- JuhJuh connects to the VM
- Pulls the new image
- Stops the current containers
- Starts the new containers
- Runs the health check
- Reports success or failure
Downtime window: Between step 3 and step 5. Typically 5 to 30 seconds depending on startup time.
When to use: Staging environments, internal tools, development VMs, and any workload where a few seconds of downtime per deploy is acceptable.
This is the default behavior described in Deployments. No additional configuration needed.
Rolling deployment¶
A single-VM strategy that reduces downtime by starting the new version before stopping the old one. JuhJuh brings up new containers alongside existing ones, verifies they pass health checks, then drains and removes the old containers.
How it works:
- JuhJuh connects to the VM
- Pulls the new image
- Starts new containers alongside the current ones
- Waits for new containers to pass health checks
- Drains connections from old containers (respects in-flight requests)
- Stops and removes old containers
- Reports success
Downtime window: Near zero. A brief moment during the drain phase where new connections route to the new version while old connections finish on the previous version. WebSocket connections on old containers are closed gracefully after the drain timeout.
When to use: Production workloads on a single VM that need minimal disruption without the cost of additional infrastructure.
Configure rolling deployment¶
Dashboard:
- Open your deployment
- Under Strategy, select Rolling
- Set the health check path (e.g.,
/health/) - Set the drain timeout in seconds (default: 30). In-flight requests get this long to finish before the old containers are stopped
- Deploy as normal
JuhJuh File:
deployments:
production-app:
vm: production-app
image: registry.example.com/my-app
tag: latest
strategy: rolling
healthCheck:
path: /health/
interval: 5
timeout: 10
retries: 3
drainTimeout: 30
Blue/green deployment¶
Two identical application VMs sit behind a dedicated load balancer. One VM is live (serving traffic), the other is idle (a hot spare running the previous version). Deploying means updating the idle VM, verifying it works, and switching traffic in one atomic operation. Zero downtime, instant rollback.
How it works:
sequenceDiagram
participant You
participant JuhJuh
participant LB as Load Balancer VM
participant Idle as Idle App VM
participant Live as Live App VM
You->>JuhJuh: Deploy new version
JuhJuh->>Idle: Pull image, start services
JuhJuh->>Idle: Run health checks
Idle-->>JuhJuh: Healthy
JuhJuh->>LB: Switch traffic to Idle
LB->>LB: Drain connections from Live
Note over LB: Reload completes in <1s
Idle-->>You: Now serving traffic
Note over Live: Becomes idle hot spareDowntime window: None. The load balancer only switches traffic after the newly deployed VM passes health checks. In-flight requests on the previously live VM finish before it goes idle.
Rollback: Seconds. JuhJuh switches traffic back to the other VM, which still runs the previous version. No image pull, no container restart. Just a traffic switch.
What you need¶
| VM | Role | Always running |
|---|---|---|
| Load balancer VM | Routes traffic to the active application VM. Handles SSL termination and health monitoring | Yes |
| Application VM (blue) | Runs your application. Alternates between live and idle | Yes |
| Application VM (green) | Runs your application. Alternates between live and idle | Yes |
All three VMs are persistent. JuhJuh doesn't create or destroy VMs during deployment. The load balancer VM runs a minimal traffic routing service with no application code.
Architecture¶
graph TD
Internet[Internet] --> StaticIP[Static IP]
StaticIP --> LB[Load Balancer VM]
LB -->|active| Blue[App VM - Blue]
LB -.->|idle| Green[App VM - Green]
Blue --> DB[(Database)]
Green --> DB
Blue --> Cache[(Cache)]
Green --> CacheThe static IP is attached to the load balancer VM, not the application VMs. DNS always points to the load balancer. Switching between blue and green never changes the IP your users connect to.
Configure blue/green deployment¶
Dashboard:
- Provision three VMs: one for the load balancer, two for the application
- Navigate to Deployments
- Click Create deployment
- Under Strategy, select Blue/Green
- Select the load balancer VM
- Select both application VMs (blue and green)
- Set the health check path
- Set the drain timeout
- Deploy. JuhJuh handles the rest
JuhJuh File:
vms:
production-lb:
name: my-app-lb
env_type: production
cpu: 1
memory_gb: 1
disk_gb: 10
region: europe-west
production-blue:
name: my-app-blue
env_type: production
cpu: 4
memory_gb: 16
disk_gb: 200
region: europe-west
production-green:
name: my-app-green
env_type: production
cpu: 4
memory_gb: 16
disk_gb: 200
region: europe-west
deployments:
production-app:
strategy: blue-green
loadBalancer: production-lb
targets:
blue: production-blue
green: production-green
image: registry.example.com/my-app
tag: latest
vault: app-vault
services: [web, worker]
healthCheck:
path: /health/
interval: 5
timeout: 10
retries: 3
drainTimeout: 30
Deploy flow¶
- JuhJuh identifies the idle VM (the one not currently receiving traffic)
- Connects to the idle VM and pulls the new image
- Starts the application services
- Runs health checks against the idle VM directly (not through the load balancer)
- If healthy, tells the load balancer to switch traffic to the newly deployed VM
- The load balancer drains in-flight connections from the previously live VM
- The previously live VM becomes the idle hot spare, still running the previous version
- Deployment is marked as successful
If the health check fails in step 4, the deployment is marked as failed. Traffic stays on the live VM. Your users don't notice a thing.
Rollback¶
- Click Rollback on the deployment dashboard (or run
juhjuh deploy rollback production-app) - JuhJuh switches traffic back to the other VM, which still runs the previous version
- The rollback completes in seconds, no image pull required
For deeper rollbacks (to a version older than the one on the idle VM), JuhJuh deploys the selected version to the idle VM first, health checks it, then switches.
Database migration safety¶
No deployment strategy protects you from a breaking database migration. If your migration locks a table or removes a column the running application still reads, you'll have downtime regardless of how you switch traffic.
Safe migration practices:
- Expand first. Add new columns, tables, or indexes in one deploy. Make them nullable or give them defaults so the current application version ignores them
- Migrate code. Deploy the new application version that uses the new schema
- Contract later. Remove old columns or constraints in a follow-up deploy, after you've confirmed the new version is stable
This expand/migrate/contract pattern keeps old and new application versions compatible with the same database schema at all times.
Avoid in a single deploy:
- Renaming columns (breaks the running version immediately)
- Dropping columns the current code still reads
- Adding
NOT NULLconstraints without a default value - Long-running data backfills that lock tables
Session and state considerations¶
Blue/green deployments route traffic to a different VM after the switch. If your application stores session data in process memory, users get logged out when traffic moves.
Before enabling blue/green, confirm your sessions are stored externally (in your database or cache). JuhJuh vault secrets are injected into both application VMs, so both connect to the same session store.
WebSocket connections on the previously live VM close during the drain phase. Clients should reconnect automatically. If your client-side code doesn't handle reconnection, users will need to refresh the page after a deploy.
Cost comparison¶
Assuming identical application VM specs:
| Strategy | VMs | Relative cost |
|---|---|---|
| Standard | 1 app | 1x |
| Rolling | 1 app | 1x |
| Blue/Green | 1 LB + 2 app | ~2.1x (LB VM is minimal spec) |
The load balancer VM only routes traffic. It needs minimal compute (1 CPU, 1 GB RAM, 10 GB disk). The additional cost comes primarily from the second application VM, which mirrors the first.
If you want zero-downtime but can't justify the cost of a third VM, rolling gets you close on a single machine.
CLI reference¶
# Deploy with strategy override
juhjuh deploy create production-app --strategy blue-green --wait
# Check which VM is currently live
juhjuh deploy status production-app
# Rollback (blue/green: instant traffic switch)
juhjuh deploy rollback production-app --wait
# Drain and switch manually (blue/green only)
juhjuh deploy switch production-app --target blue --wait
Load balancer options¶
When you select blue/green, JuhJuh provisions a traffic routing service on your load balancer VM. Two routing engines are available.
| Standard proxy | Advanced balancer | |
|---|---|---|
| Approach | Lightweight reverse proxy | Purpose-built load balancer |
| Active health checks | Script-driven (JuhJuh checks health before switching) | Built-in (continuously monitors both VMs) |
| Connection draining | Graceful reload, in-flight requests complete | First-class drain state with per-connection tracking |
| SSL termination | Excellent, widely documented | Good, slightly more configuration |
| WebSocket support | Full support, explicit configuration | Full support, native handling |
| Stats and monitoring | Basic; extend with observability tools | Rich built-in stats dashboard |
| Memory footprint | ~2-5 MB | ~5-10 MB |
| Configuration | Simpler, fewer directives | More verbose, more granular control |
| Switchover mechanism | Rewrite upstream config, reload (<1s) | Update server state via socket, reload (<1s) |
Standard proxy is the default. One upstream definition, one reload command, and JuhJuh handles health verification before the switch. This covers the blue/green use case with minimal moving parts.
Advanced balancer is for teams that need continuous health monitoring at the load balancer level, fine-grained connection draining, or a built-in stats dashboard without additional tooling. Select it during load balancer VM setup or set load_balancer_engine in your JuhJuh File:
deployments:
production-app:
strategy: blue-green
loadBalancer: production-lb
load_balancer_engine: advanced # default: standard
targets:
blue: production-blue
green: production-green
Both engines deliver the same outcome: zero-downtime switchover with instant rollback. The difference is in how health monitoring and connection draining work. JuhJuh configures either engine automatically when provisioning the load balancer VM.
Load balancer as a single point of failure¶
With blue/green, the load balancer VM is the single point through which all traffic flows. If it reboots, traffic stops until it comes back (typically 30 to 60 seconds).
For most teams, this is fine. VM reboots are rare and recovery is fast. If your uptime requirements demand more, two options exist:
-
Managed cloud load balancer. Place a managed load balancing service in front of your JuhJuh load balancer VM. The managed service handles failover automatically with no VM to maintain. This adds a layer of indirection but removes the single point of failure entirely.
-
Dual load balancers. Provision two load balancer VMs behind a forwarding rule. If one goes down, the other continues serving. More complex to configure and doubles the load balancer cost, but keeps everything on VMs you control.
Most teams don't need either. Start with a single load balancer VM and revisit if you have contractual uptime SLAs.
Choosing a strategy¶
Start with standard. It works, it's simple, and a few seconds of downtime per deploy is fine for most workloads.
Move to rolling when your users start noticing deploys, when you deploy frequently during business hours, or when you run WebSocket connections that shouldn't drop unnecessarily.
Move to blue/green when you have strict uptime SLAs, when rollbacks need to be instant, or when you can't afford to discover a bad deploy through user-facing errors. The extra cost is the price of confidence.
You can use different strategies for different deployments in the same organization. Run blue/green for your production API and standard for your staging environment.
Related¶
- Deployments: core deployment concepts, versioning, and execution logs
- VMs: provision the infrastructure your deployments run on
- Vault: secrets injected into all VMs at deploy time
- Networking: private networking between load balancer and application VMs
- Dependencies: coordinate deploy order across multiple services
- JuhJuh File: define your full deployment strategy in code
- CLI: trigger deployments, rollbacks, and traffic switches from your terminal
- Infrastructure Overview: how deployment strategies fit into the broader stack