OpenStack Error Guide: 'Layer7 wrong status' — HAProxy Backend DOWN on the Control-Plane VIP
Fix HAProxy backends marked DOWN on the OpenStack control-plane VIP causing 503s and timeouts: diagnose dead service containers, OOM kills, failed health checks, and tight timeouts.
- #openstack
- #troubleshooting
- #errors
- #haproxy
Stuck on this OpenStack error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
In a Kolla-Ansible deployment, every OpenStack API is fronted by HAProxy on a control-plane VIP (managed by keepalived). HAProxy load-balances each API — Keystone, Nova, Neutron, Glance, Cinder — across the controller nodes. When the backend servers for a service fail their health checks, HAProxy marks them DOWN, and once every server in a backend is down HAProxy returns 503 Service Unavailable or the request simply times out.
The literal errors you will see in the HAProxy log:
Server nova_api/controller-02 is DOWN, reason: Layer7 wrong status, code: 503, check duration: 4ms. 2 active and 0 backup servers left. 0 sessions active...
backend nova_api has no server available!
And from the client side hitting the VIP:
$ openstack server list
Service Unavailable (HTTP 503)
The tell is that the API on the VIP fails, but the service container on an individual controller may still be reachable directly on its own host/port. The problem is between HAProxy and the backend — the backend service is dead, unhealthy, or too slow — not HAProxy itself.
Symptoms
- API calls through the VIP return
503 Service Unavailableor hang until the client times out. docker logs haproxyshowsServer <backend>/<host> is DOWNandbackend <name> has no server available!.- One service is affected (e.g. only Glance 503s) while others on the same VIP work fine.
- The affected service’s container is
Restarting,Exited, or unresponsive on a controller.
docker exec haproxy sh -c 'echo "show stat" | socat stdio /var/lib/kolla/haproxy/haproxy.sock' \
| awk -F, '$18=="DOWN" || $18=="MAINT" {print $1"/"$2" -> "$18}'
glance_api/controller-01 -> DOWN
glance_api/controller-02 -> DOWN
glance_api/controller-03 -> DOWN
All three servers DOWN in one backend means every request to that service 503s.
Common Root Causes
1. The backend service container is down or OOM-killed
If the API container crashed, was OOM-killed, or is stuck restarting, HAProxy’s health check fails and marks it DOWN.
docker ps -a --filter name=glance_api --format '{{.Names}} {{.Status}}'
docker inspect glance_api --format '{{.State.OOMKilled}} {{.State.ExitCode}}'
glance_api Restarting (137) 6 seconds ago
true 137
Exit 137 with OOMKilled=true means the kernel killed it for memory.
2. The service is up but failing its health check
Kolla health checks hit an API path; if the service returns 500/503 (e.g. it can’t reach its database or Keystone), HAProxy sees Layer7 wrong status and marks it DOWN even though the process is running.
docker exec haproxy sh -c 'echo "show stat" | socat stdio /var/lib/kolla/haproxy/haproxy.sock' \
| awk -F, '$1=="glance_api"{print $2, $18, $37, $38}' # svname status check_status last_chk
controller-01 DOWN L7STS L7STS 503
L7STS / code 503 means the backend answered but with a bad HTTP status — look at that service’s own log next.
3. A dependency the backend needs is broken
The container is healthy but its own downstream — MariaDB (Galera), RabbitMQ, or Keystone — is unreachable, so it returns errors on every request.
docker logs glance_api 2>&1 | grep -iE "error|refused|timeout|unavailable" | tail -5
ERROR ... OperationalError: (2003, "Can't connect to MySQL server on '<vip>' (111)")
4. HAProxy timeouts too tight for a slow backend
A slow image upload or a heavy Nova list can exceed timeout server/timeout client in haproxy.cfg, so HAProxy tears the connection down and the client sees a timeout or 504.
docker exec haproxy grep -E 'timeout (client|server|connect|check)' /etc/haproxy/haproxy.cfg
timeout client 1m
timeout server 1m
timeout connect 10s
A 1-minute timeout server will cut off a large Glance upload mid-flight.
5. Firewall / network between HAProxy and the backend host
A firewall change or routing blip blocks HAProxy from reaching the backend port, producing Layer4 connection problem in the check status.
docker exec haproxy sh -c 'echo "show stat" | socat stdio /var/lib/kolla/haproxy/haproxy.sock' \
| awk -F, '$1=="nova_api"{print $2,$18,$38}'
controller-03 DOWN L4CON
L4CON (Layer4 connection problem) points at network/port reachability, not the app.
Diagnostic Workflow
Step 1: Ask HAProxy which backends are down (read-only)
docker exec haproxy sh -c 'echo "show stat" | socat stdio /var/lib/kolla/haproxy/haproxy.sock' \
| awk -F, 'NR==1{next} $2!="FRONTEND" && $2!="BACKEND" {print $1"/"$2" -> "$18" ("$38")"}'
$18 is status (UP/DOWN), $38 is the check result (L7STS, L4CON, L7OK). This tells you whether the failure is app-layer or network-layer.
Step 2: Read the HAProxy log for the transition and reason
docker logs haproxy 2>&1 | grep -iE "is DOWN|no server available" | tail -10
Layer7 wrong status, code: 503 → the backend app is unhealthy. Layer4 connection problem → network/port. Layer7 timeout → the backend is too slow.
Step 3: Inspect the failed backend container
docker ps -a --filter name=glance_api --format '{{.Names}} {{.Status}}'
docker inspect glance_api --format 'OOM={{.State.OOMKilled}} Exit={{.State.ExitCode}} Restarts={{.RestartCount}}'
docker logs glance_api 2>&1 | tail -30
Step 4: Test the backend directly, bypassing HAProxy
# From the controller, hit the service on its real bind port (not the VIP)
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:9292/ # glance-api example
A 300/200 direct but 503 via the VIP confirms the health check / HAProxy path; a failure direct confirms the service itself.
Step 5: Check the service’s own dependencies
docker logs glance_api 2>&1 | grep -iE "OperationalError|MessagingTimeout|Unauthorized|refused" | tail -5
docker exec haproxy grep -E 'timeout (server|client|connect)' /etc/haproxy/haproxy.cfg
Example Root Cause Analysis
At 14:20 an operator reports openstack image list returns 503 Service Unavailable, while openstack server list works. Only Glance is affected. Querying the HAProxy stats socket:
docker exec haproxy sh -c 'echo "show stat" | socat stdio /var/lib/kolla/haproxy/haproxy.sock' \
| awk -F, '$1=="glance_api"{print $2,$18,$38}'
controller-01 DOWN L7STS
controller-02 DOWN L7STS
controller-03 DOWN L7STS
All three glance backends are DOWN with L7STS — the app answers but with a bad status, so this is not a network issue. Checking one container:
docker inspect glance_api --format 'OOM={{.State.OOMKilled}} Exit={{.State.ExitCode}}'
docker logs glance_api 2>&1 | tail -5
OOM=false Exit=0
ERROR glance.common.wsgi ... OperationalError: (2003, "Can't connect to MySQL server on '<vip>' (111)")
Glance is up but cannot reach its database on the VIP, so it 503s every health check. The real fault is one step down: a Galera node was drained and the MariaDB backend in HAProxy has no server available either. The narrowest correct fix is to restore the database backend (bring the Galera node back / repair the cluster), not to touch Glance or HAProxy. Once MariaDB answers on the VIP, Glance’s health checks pass and HAProxy re-marks the backends UP:
docker exec haproxy sh -c 'echo "show stat" | socat stdio /var/lib/kolla/haproxy/haproxy.sock' \
| awk -F, '$1=="glance_api"{print $2,$18}' # controllers flip back to UP
openstack image list # succeeds
If a single service container were genuinely wedged (not a dependency), the narrowest action is to restart just that one backend container — for example docker restart glance_api on the affected controller — never docker restart haproxy, which would drop every service on the VIP at once.
Prevention Best Practices
- Monitor the HAProxy stats socket directly: alert on any backend
DOWNand onbackend ... has no server available. DistinguishL7STS(app),L4CON(network), andL7TOUT(slow) in the alert. - Set memory limits and monitoring on API containers so an OOM (exit 137) is caught before it takes a whole backend down.
- Alert on the shared dependencies (MariaDB/Galera, RabbitMQ, Keystone) separately — a single 503 storm across many services usually points at one of them, not the APIs.
- Right-size HAProxy timeouts: raise
timeout serverfor services with large payloads (Glance uploads) rather than letting big requests get cut off. - Never blind-restart
haproxyto “fix” a 503 — it drops every service on the VIP. Restart only the specific failed backend container after confirming it, and prefer fixing the dependency. - Keep at least two healthy backends per service so a single controller reboot never empties a backend. See more OpenStack guides.
Quick Command Reference
# Which backends are DOWN and why (read-only)
docker exec haproxy sh -c 'echo "show stat" | socat stdio /var/lib/kolla/haproxy/haproxy.sock' \
| awk -F, '$18=="DOWN"{print $1"/"$2" "$38}'
# HAProxy log: transitions and empty backends
docker logs haproxy 2>&1 | grep -iE "is DOWN|no server available" | tail -10
# Inspect the failing backend container
docker ps -a --filter name=<service>_api --format '{{.Names}} {{.Status}}'
docker inspect <service>_api --format 'OOM={{.State.OOMKilled}} Exit={{.State.ExitCode}}'
docker logs <service>_api 2>&1 | tail -30
# Bypass the VIP: test the backend directly on the controller
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:<port>/
# Check timeouts in haproxy.cfg
docker exec haproxy grep -E 'timeout (client|server|connect|check)' /etc/haproxy/haproxy.cfg
# Recover: restart ONLY the failed backend service (not haproxy)
docker restart <service>_api
Conclusion
An HAProxy Server ... is DOWN / backend has no server available on the control-plane VIP means the backend behind a specific OpenStack API failed its health check — HAProxy is doing its job. The diagnostic signature is a single service 503-ing on the VIP while others work. Typical root causes:
- The backend API container is down, restarting, or OOM-killed (exit 137).
- The container is up but returns a bad HTTP status (
L7STS) on its health check. - A dependency the backend needs — MariaDB/Galera, RabbitMQ, Keystone — is unreachable.
- HAProxy
timeout server/timeout clientis too tight for a slow request. - A firewall/routing change blocks HAProxy from the backend port (
L4CON).
Read the stats socket and log first — the check status (L7STS vs. L4CON vs. L7TOUT) tells you whether you’re chasing the app, the network, or a timeout. Then restart the narrowest thing: the failed backend service, never HAProxy itself.
Fixed it? Get 500 OpenStack & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.