How to run a blue-green deployment cutover with HAProxy

Blue-green cutovers become risky when HAProxy can reach both application versions but the release operator has no single traffic switch or rollback proof. A map-backed route gives the release window one active backend name, so normal requests stay on blue until the runtime map is moved to green.

HAProxy map files can return the backend name used by use_backend, and the Runtime API can change one map entry in running memory with set map. Health checks on both backend pools keep the switch tied to application readiness instead of only proving that a TCP port accepts connections.

Runtime API changes do not update the map file on disk, so persistence is a separate action after smoke tests pass. Keep the disk map pointed at blue during the first green check when a reload-based rollback is acceptable, then write green to disk only after the release owner accepts the cutover.

Steps to run a blue-green deployment cutover with HAProxy:

  1. Install socat if the HAProxy host does not already have a Runtime API socket client.
    $ sudo apt install socat
  2. Create the map directory for release state.
    $ sudo install -d -m 0755 /etc/haproxy/maps
  3. Create the release map with the current production backend.
    /etc/haproxy/maps/bluegreen.map
    active backend_blue

    The key can be any fixed value. The backend name on the right side is the part HAProxy will route to.

  4. Add the runtime socket, map-backed frontend rule, and blue and green backend pools to the HAProxy configuration.
    /etc/haproxy/haproxy.cfg
    global
      stats socket /run/haproxy/admin.sock mode 660 level admin
     
    defaults
      mode http
      timeout connect 5s
      timeout client 30s
      timeout server 30s
      default-server inter 5s fall 3 rise 2
     
    frontend fe_http
      bind :80
      use_backend %[str(active),map(/etc/haproxy/maps/bluegreen.map,backend_blue)]
     
    backend backend_blue
      option httpchk GET /healthz
      http-check expect status 200
      server blue1 10.10.10.11:8080 check
      server blue2 10.10.10.12:8080 check
     
    backend backend_green
      option httpchk GET /healthz
      http-check expect status 200
      server green1 10.10.20.11:8080 check
      server green2 10.10.20.12:8080 check

    Replace the backend addresses, ports, and health URI with the real application endpoints. The runtime socket uses level admin because set map changes live routing state.

  5. Validate the complete HAProxy configuration.
    $ sudo haproxy -c -V -f /etc/haproxy/haproxy.cfg
    Configuration file is valid
  6. Reload HAProxy to load the map file and runtime socket.
    $ sudo systemctl reload haproxy
  7. Confirm that HAProxy loaded the blue release map.
    $ echo "show map /etc/haproxy/maps/bluegreen.map" | sudo socat stdio unix-connect:/run/haproxy/admin.sock
    0x55d0b5e8d4b0 active backend_blue
  8. Confirm that the green backend passes HAProxy health checks before moving traffic.
    $ echo "show stat backend_green 4 -1" | sudo socat stdio unix-connect:/run/haproxy/admin.sock
    # pxname,svname,qcur,qmax,scur,smax,slim,stot,bin,bout,dreq,dresp,ereq,econ,eresp,wretr,wredis,status,weight,act,bck,chkfail,chkdown,lastchg,downtime,qlimit,pid,iid,sid,throttle,lbtot,tracked,type,rate,rate_lim,rate_max,check_status,check_code,check_duration
    ##### snipped #####
    backend_green,green1,0,0,0,0,,0,0,0,,0,,0,0,0,0,UP,1,1,0,0,0,2,0,,1,4,1,,0,,2,0,,0,L7OK,200,0
    backend_green,green2,0,0,0,0,,0,0,0,,0,,0,0,0,0,UP,1,1,0,0,0,2,0,,1,4,2,,0,,2,0,,0,L7OK,200,0
    ##### snipped #####

    Do not cut over while a green server row is DOWN, MAINT, DRAIN, or still waiting for the required rise count.

  9. Smoke test the current blue path through the public listener.
    $ curl http://www.example.com/release
    version=blue
  10. Move live traffic to the green backend through the Runtime API.
    $ echo "set map /etc/haproxy/maps/bluegreen.map active backend_green" | sudo socat stdio unix-connect:/run/haproxy/admin.sock

    A successful set map command can return no text. The next step checks the runtime state instead of relying on command output.

  11. Confirm that the runtime map now points to green.
    $ echo "show map /etc/haproxy/maps/bluegreen.map" | sudo socat stdio unix-connect:/run/haproxy/admin.sock
    0x55d0b5e8d4b0 active backend_green
  12. Smoke test the green path through the same public listener.
    $ curl http://www.example.com/release
    version=green
  13. Persist the green cutover on disk after the smoke test passes.
    /etc/haproxy/maps/bluegreen.map
    active backend_green

    The Runtime API update is in memory only. A reload or restart will read this file again, so leaving it on backend_blue turns the next reload into a rollback.

  14. Roll back during the smoke window if green returns the wrong version or fails the release check.
    $ echo "set map /etc/haproxy/maps/bluegreen.map active backend_blue" | sudo socat stdio unix-connect:/run/haproxy/admin.sock
  15. Verify that rollback sends traffic back to blue.
    $ curl http://www.example.com/release
    version=blue
  16. Persist the rollback on disk if blue remains the active release.
    /etc/haproxy/maps/bluegreen.map
    active backend_blue