This episode automates the HAProxy configuration lifecycle: syntax validation in CI pipelines, safe rollout strategies like blue-green and canary, and testing changes before they touch production.

HAProxy configuration is code: it must be reviewed, tested, and deployed regularly. Episode 20 covers how to treat haproxy.cfg like a proper software product.
You'll build a validation pipeline in CI, choose rollout strategies that don't disrupt traffic, and test changes on a staging environment before production. The single goal: configuration changes should no longer be a nerve-racking moment.
It all starts with one command you've used often: haproxy -c. In a pipeline, that command becomes the main gate:
name: validate-haproxy
on:
pull_request:
paths:
- "haproxy.cfg"
- "maps/*.map"
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate syntax
run: |
docker run --rm -v "$PWD:/config:ro" \
haproxy:2.9-alpine \
haproxy -c -f /config/haproxy.cfgThe workflow above runs validation every time the configuration files change. haproxy -c -f /config/haproxy.cfg runs inside a container using the same HAProxy version as production.
Version differences can make a configuration pass local validation but fail in production. Therefore:
haproxy -c -d command to see directive evaluation.haproxy -c -d -f /etc/haproxy/haproxy.cfghaproxy -c -d -f /etc/haproxy/haproxy.cfg validates and also shows the evaluation result. Useful for catching mistyped directives early.
The blue-green pattern keeps two configurations alive: the old and the new. Traffic is switched as a single unit:
haproxy -c -f /etc/haproxy/haproxy-green.cfg || exit 1
cp /etc/haproxy/haproxy-green.cfg /etc/haproxy/haproxy.cfg
systemctl reload haproxyhaproxy -c -f /etc/haproxy/haproxy-green.cfg || exit 1 makes sure the new configuration is valid before replacing the active file. Rollback is just restoring the old file and reloading.
Canary is smoother: a small share of traffic is directed to servers with the new configuration or version, the rest stays on the old:
backend web_back
balance roundrobin
server stable 10.0.0.11:8080 check weight 90
server canary 10.0.0.12:8080 check weight 10The configuration above sends 10 percent of traffic to the canary server. If it stays healthy, raise its weight gradually:
echo "set weight web_back/canary 50" | socat stdio /run/haproxy.sockecho "set weight web_back/canary 50" | socat stdio /run/haproxy.sock shifts traffic without a reload. If a problem appears, drop the weight back to zero.
If the cluster runs many HAProxy nodes (episode 13), rollout happens node by node:
echo "set server web_back/node1 state drain" | socat stdio /run/haproxy.sock
systemctl restart haproxy
echo "set server web_back/node1 state ready" | socat stdio /run/haproxy.sockThe drain, restart, ready sequence updates one node without taking down the service as a whole.
Meaningful testing needs an environment that resembles production:
Beyond syntax, test behavior: routing must reach the correct backend.
curl -s -o /dev/null -w "%{http_code}\n" http://staging/api/health
curl -s -o /dev/null -w "%{http_code}\n" http://staging/admin/healthcurl -s -o /dev/null -w "%{http_code}\n" compares statuses across paths. These tests can be repeated in production after deploy for a final confirmation.
Before releasing to production, make sure:
Episode 20 turns HAProxy configuration into an asset managed like code: automated validation in CI, staged rollouts that are easy to roll back, and tests that guarantee behavior before production.
Key takeaways:
haproxy -c a mandatory gate in the CI pipeline.In the next episode we'll cover SLOs, SLIs & operational metrics — defining SLOs for HAProxy traffic, building alerting on error rate, latency, and resource saturation, and writing runbooks for traffic incidents and failover.