HAProxy ACL-Based Routing For Blue-Green Deployments Without Downtime
HAProxy ACL-Based Routing for Blue-Green Deployments Without Downtime
Blue-green deployments are one of the cleanest ways to ship without waking anyone up at 3am. HAProxy makes this surprisingly straightforward with ACL-based routing — no service mesh required, no Kubernetes magic needed.
Here's your quick reference for wiring it up.
The Core Concept
You run two identical environments — blue (current production) and green (new version). HAProxy sits in front and decides which backend gets traffic. You shift the needle gradually or all at once, validate, then decommission the old environment.
Client → HAProxy → [Blue Backend] (current)
→ [Green Backend] (new version)
Minimal HAProxy Config Structure
global
log /dev/log local0
maxconn 50000
daemon
defaults
mode http
timeout connect 5s
timeout client 30s
timeout server 30s
option httplog
option forwardfor
option http-server-close
frontend web_frontend
bind *:80
bind *:443 ssl crt /etc/ssl/certs/your-cert.pem
# ACL definitions go here
# Backend routing goes here
backend blue
server blue-01 10.0.1.10:8080 check
server blue-02 10.0.1.11:8080 check
backend green
server green-01 10.0.2.10:8080 check
server green-02 10.0.2.11:8080 check
ACL Routing Patterns
1. Route by Header (Canary Testing)
Send internal testers to green while everyone else hits blue:
frontend web_frontend
bind *:80
acl is_canary hdr(X-Canary) -i true
acl is_internal src 10.0.0.0/8
use_backend green if is_canary
use_backend green if is_internal
default_backend blue
2. Route by Cookie (Sticky Canary Sessions)
Persist users to green once they're assigned — avoids session confusion:
frontend web_frontend
bind *:80
acl on_green hdr_sub(Cookie) "deployment=green"
use_backend green if on_green
default_backend blue
Your app (or HAProxy itself) sets Set-Cookie: deployment=green for the canary cohort.
3. Percentage-Based Traffic Split
Use nbsrv and weight tricks, or the cleaner random approach:
frontend web_frontend
bind *:80
# Send ~10% to green using random balancing
use_backend green if { rand(10) eq 0 }
default_backend blue
For more precise control, use backend weights:
backend traffic_split
balance roundrobin
server blue-01 10.0.1.10:8080 check weight 90
server green-01 10.0.2.10:8080 check weight 10
4. Route by URL Path
Useful when you're migrating specific endpoints first:
frontend web_frontend
bind *:80
acl is_api path_beg /api/v2
acl is_new_dashboard path_beg /dashboard/new
use_backend green if is_api
use_backend green if is_new_dashboard
default_backend blue
5. Route by Query Parameter
frontend web_frontend
bind *:80
acl wants_green urlp(version) green
use_backend green if wants_green
default_backend blue
Hit https://yourapp.com/feature?version=green to force green routing.
Zero-Downtime Cutover via Runtime API
HAProxy's Runtime API lets you shift traffic without reloading config:
# Connect to the HAProxy stats socket
echo "show info" | socat stdio /var/run/haproxy/admin.sock
# Drain blue servers (removes from active rotation gracefully)
echo "set server blue/blue-01 state drain" | socat stdio /var/run/haproxy/admin.sock
echo "set server blue/blue-02 state drain" | socat stdio /var/run/haproxy/admin.sock
# Bring green servers to ready state
echo "set server green/green-01 state ready" | socat stdio /var/run/haproxy/admin.sock
echo "set server green/green-02 state ready" | socat stdio /var/run/haproxy/admin.sock
# If you need to roll back immediately
echo "set server blue/blue-01 state ready" | socat stdio /var/run/haproxy/admin.sock
echo "set server green/green-01 state drain" | socat stdio /var/run/haproxy/admin.sock
drain state lets existing connections finish — new connections stop going to that server. Clean, no dropped requests.
Health Check Configuration
Don't route to green until it's actually ready:
backend green
option httpchk GET /health HTTP/1.1\r\nHost:\ yourapp.com
http-check expect status 200
default-server inter 2s fall 3 rise 2
server green-01 10.0.2.10:8080 check
server green-02 10.0.2.11:8080 check
inter 2s— check every 2 secondsfall 3— mark down after 3 consecutive failuresrise 2— mark up after 2 consecutive successes
Graceful Config Reload (When You Must)
If you do need to reload config (ACL changes, new server additions):
# Validate config first — always
haproxy -c -f /etc/haproxy/haproxy.cfg
# Graceful reload — zero dropped connections
systemctl reload haproxy
# Or the manual way
haproxy -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid)
The -sf flag sends a SIGUSR1 to old processes, telling them to finish current connections and exit.
Quick Rollback Checklist
| Step | Command |
|---|---|
| Drain green | set server green/green-01 state drain |
| Restore blue | set server blue/blue-01 state ready |
| Verify traffic | echo "show stat" | socat stdio /var/run/haproxy/admin.sock |
| Check error rates | Tail your access log: tail -f /var/log/haproxy.log |
Key Takeaways
- Use
drainovermaint— drain finishes existing connections, maint kills them - Test your ACLs with
haproxy -cbefore every reload - Health checks are non-negotiable — never route to a server that hasn't passed at least
rise 2checks - Start with header/cookie routing for your first blue-green — it's the most controllable
The Runtime API is your best friend here. Master socat + the admin socket and you can shift traffic in milliseconds without touching a config file.
Was this article helpful?
Data Infrastructure Engineer
Your data is only as good as the infrastructure it sits on. I specialize in PostgreSQL, Redis, database migrations, backup strategies, and making sure your data survives whatever chaos your application throws at it.
Related Articles
HAProxy Stick Tables For Session Persistence And DDoS Rate Limiting
If you've been running HAProxy in production for any length of time, you've probably hit one of two problems: either your users keep losing their sessions...
Fix HAProxy '503 Service Unavailable' Backend Down
Diagnose and fix HAProxy 503 Service Unavailable errors caused by backend servers failing health checks, connection limits, or misconfigured routing.
HAProxy Health Checks and Automatic Failover
Configure HAProxy health checks at TCP, HTTP, and application layers with automatic failover, backup servers, and alerting for production reliability.
HAProxy SSL Termination: Offload TLS Without the Headache
Configure HAProxy SSL termination to offload TLS from backend servers, including certificate management, hardening, and SNI-based routing.
HAProxy Load Balancing: From Installation to Production
Configure HAProxy for HTTP and TCP load balancing — installation, frontends, backends, health checks, ACLs, SSL termination, and the stats dashboard.
Git Worktrees For Managing Multiple Feature Branches Simultaneously
Stop stashing. Stop context-switching. Git worktrees let you check out multiple branches simultaneously in separate directories — each with its own working...