--- title: "Health checks y failover en HAProxy con Ubuntu 24.04" description: "Configura health checks HTTP y TCP en HAProxy sobre Ubuntu 24.04, con servidor de respaldo, página de mantenimiento y drenado de servidores sin cortes." category: "Proxy y Equilibrio de Carga" language: es source: https://cubepath.com/es/docs/proxy-y-equilibrio-de-carga/verificaciones-de-salud-de-haproxy-y-conmutacion-por-error updated: 2026-09-25 --- # Health checks y failover en HAProxy con Ubuntu 24.04 Un balanceador solo aporta alta disponibilidad si deja de enviar tráfico a los servidores que fallan. HAProxy lo hace con comprobaciones de salud activas: consulta periódicamente cada backend y lo saca de la rotación cuando no responde como se espera, sin esperar a que falle una petición real. En este tutorial configurarás en Ubuntu 24.04 comprobaciones HTTP con umbrales de caída y recuperación, un servidor de respaldo, una página de mantenimiento cuando no queda ningún backend y el drenado manual de servidores para hacer mantenimiento sin cortar conexiones. Todo se prueba con un laboratorio de tres backends en la misma máquina. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - El puerto 80 libre. En producción los backends estarán en otras máquinas, normalmente en una red privada; bastará con cambiar las direcciones de las líneas `server`. En el laboratorio son tres sitios de Nginx escuchando en `127.0.0.1`. ## Paso 1: Instalar HAProxy, Nginx y socat Ubuntu 24.04 incluye HAProxy 2.8 LTS. Nginx servirá los backends de prueba y `socat` permite hablar con el socket de administración de HAProxy: ```bash sudo apt update sudo apt install haproxy nginx socat ``` Desactiva el sitio por defecto de Nginx, que ocupa el puerto 80 que usará HAProxy: ```bash sudo rm /etc/nginx/sites-enabled/default sudo systemctl reload nginx ``` Si usas UFW, permite HTTP: ```bash sudo ufw allow 80/tcp ``` ## Paso 2: Crear backends con un endpoint de salud Una buena comprobación de salud no se limita a ver si el puerto responde: consulta una ruta que la propia aplicación controla. Así la aplicación puede declararse no disponible (por ejemplo, si pierde la conexión con la base de datos) y también puedes sacar un servidor de servicio a propósito. En el laboratorio, `/health` devuelve `200` mientras exista el archivo `/var/www/backendN/health` y `503` si se borra. Crea los archivos: ```bash for i in 1 2 3; do sudo mkdir -p /var/www/backend$i echo OK | sudo tee /var/www/backend$i/health > /dev/null done ``` Crea la configuración de los tres backends: ```bash sudo nano /etc/nginx/conf.d/test-backends.conf ``` ```nginx server { listen 127.0.0.1:8081; root /var/www/backend1; default_type text/plain; location = /health { try_files /health =503; } location / { return 200 "backend-1\n"; } } server { listen 127.0.0.1:8082; root /var/www/backend2; default_type text/plain; location = /health { try_files /health =503; } location / { return 200 "backend-2\n"; } } server { listen 127.0.0.1:8083; root /var/www/backend3; default_type text/plain; location = /health { try_files /health =503; } location / { return 200 "backend-3\n"; } } ``` Valida, recarga y prueba el endpoint: ```bash sudo nginx -t && sudo systemctl reload nginx curl -i http://127.0.0.1:8081/health ``` ```output HTTP/1.1 200 OK Content-Type: text/plain OK ``` ## Paso 3: Configurar HAProxy con comprobaciones HTTP Primero crea la página que HAProxy devolverá cuando no quede ningún servidor disponible. Un `errorfile` es una respuesta HTTP completa, cabeceras incluidas: ```bash sudo nano /etc/haproxy/errors/maintenance.http ``` ```text HTTP/1.1 503 Service Unavailable Content-Type: text/html; charset=utf-8 Cache-Control: no-store Connection: close Mantenimiento

Estamos en mantenimiento

Vuelve a intentarlo en unos minutos.

``` Guarda una copia de la configuración original de HAProxy y ábrela: ```bash sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.orig sudo nano /etc/haproxy/haproxy.cfg ``` Sustituye su contenido por: ```haproxy global log /dev/log local0 log /dev/log local1 notice chroot /var/lib/haproxy stats socket /run/haproxy/admin.sock mode 660 level admin stats timeout 30s user haproxy group haproxy daemon defaults log global mode http option httplog option dontlognull option redispatch retries 2 timeout connect 3s timeout client 30s timeout server 30s frontend web_in bind :80 default_backend web_servers backend web_servers balance roundrobin option httpchk http-check send meth GET uri /health ver HTTP/1.1 hdr Host localhost http-check expect status 200 default-server inter 2s fall 3 rise 2 on-marked-down shutdown-sessions errorfile 503 /etc/haproxy/errors/maintenance.http server web1 127.0.0.1:8081 check server web2 127.0.0.1:8082 check server web3 127.0.0.1:8083 check backup frontend stats bind 127.0.0.1:8404 stats enable stats uri /stats stats refresh 5s ``` Qué controla cada parte: | Directiva | Efecto | |---|---| | `option httpchk` + `http-check send` | Envía `GET /health` con cabecera `Host` a cada servidor. Sin `httpchk`, `check` solo comprueba que el puerto TCP acepta conexiones. | | `http-check expect status 200` | Solo un `200` cuenta como éxito. Sin esta línea, cualquier `2xx` o `3xx` vale. | | `inter 2s` | Intervalo entre comprobaciones. | | `fall 3` | Tres fallos seguidos marcan el servidor como `DOWN` (unos 6 segundos con `inter 2s`). | | `rise 2` | Dos éxitos seguidos lo devuelven a `UP`, para no meter en rotación un servidor que aún está arrancando. | | `on-marked-down shutdown-sessions` | Cierra las conexiones abiertas con un servidor cuando se marca como caído, en lugar de dejarlas colgadas. | | `backup` | `web3` solo recibe tráfico cuando no queda ningún servidor principal disponible. | | `option redispatch` + `retries 2` | Si falla la conexión con un servidor, la petición se reintenta en otro. | | `errorfile 503` | Página que se devuelve cuando el backend no tiene ningún servidor disponible. | `default-server` aplica esos parámetros a todas las líneas `server` del backend; puedes sobrescribirlos en una línea concreta. Valida y recarga: ```bash sudo haproxy -c -f /etc/haproxy/haproxy.cfg sudo systemctl reload haproxy ``` ```output Configuration file is valid ``` Comprueba que el tráfico se reparte entre los dos servidores principales: ```bash for i in $(seq 1 4); do curl -s http://localhost/; done ``` ```output backend-1 backend-2 backend-1 backend-2 ``` `backend-3` no aparece porque es de respaldo. ## Paso 4: Ver el estado de los servidores El socket de administración devuelve el estado de cada servidor. La salida de `show stat` es CSV; los campos 1, 2 y 18 son el backend, el servidor y su estado: ```bash echo "show stat" | sudo socat stdio /run/haproxy/admin.sock | cut -d, -f1,2,18 | grep web_servers ``` ```output web_servers,web1,UP web_servers,web2,UP web_servers,web3,UP web_servers,BACKEND,UP ``` Para ver la página de estadísticas en el navegador, abre un túnel SSH desde tu equipo con `ssh -L 8404:127.0.0.1:8404 your_user@your_server_ip` y visita `http://localhost:8404/stats`. Deja abierta una segunda terminal siguiendo el log de HAProxy para ver los cambios de estado en los pasos siguientes: ```bash sudo tail -f /var/log/haproxy.log ``` ## Paso 5: Probar la conmutación por error Simula que `web1` deja de estar sano borrando su archivo de salud: ```bash sudo rm /var/www/backend1/health ``` En unos 6 segundos (tres comprobaciones fallidas) aparece en el log: ```output Server web_servers/web1 is DOWN, reason: Layer7 wrong status, code: 503, info: "Service Temporarily Unavailable", check duration: 0ms. 1 active and 1 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue. ``` Ahora todo el tráfico va a `web2`: ```bash for i in $(seq 1 3); do curl -s http://localhost/; done ``` ```output backend-2 backend-2 backend-2 ``` Haz caer también `web2`. Al no quedar servidores principales, entra el de respaldo: ```bash sudo rm /var/www/backend2/health sleep 8 for i in $(seq 1 2); do curl -s http://localhost/; done ``` ```output backend-3 backend-3 ``` El log lo indica con `0 active and 1 backup servers left. Running on backup.` Por último, haz caer `web3`: ```bash sudo rm /var/www/backend3/health sleep 8 curl -i http://localhost/ ``` ```output HTTP/1.1 503 Service Unavailable content-type: text/html; charset=utf-8 cache-control: no-store ...

Estamos en mantenimiento

``` El log registra `backend web_servers has no server available!`. Los clientes ven tu página de mantenimiento en lugar de un error genérico. Restaura los tres servidores: ```bash for i in 1 2 3; do echo OK | sudo tee /var/www/backend$i/health > /dev/null; done ``` Tras dos comprobaciones correctas (unos 4 segundos) cada servidor vuelve a `UP`: ```output Server web_servers/web1 is UP, reason: Layer7 check passed, code: 200, check duration: 0ms. 1 active and 0 backup servers online. 0 sessions requeued, 0 total in queue. ``` ## Paso 6: Drenar un servidor para hacer mantenimiento Antes de actualizar o reiniciar un backend, conviene sacarlo de rotación de forma ordenada. El estado `drain` deja de enviarle conexiones nuevas pero respeta las que ya tiene abiertas: ```bash echo "set server web_servers/web1 state drain" | sudo socat stdio /run/haproxy/admin.sock echo "show stat" | sudo socat stdio /run/haproxy/admin.sock | cut -d, -f1,2,18 | grep web_servers ``` ```output web_servers,web1,DRAIN web_servers,web2,UP web_servers,web3,UP web_servers,BACKEND,UP ``` Cuando termines el mantenimiento, devuélvelo a la rotación: ```bash echo "set server web_servers/web1 state ready" | sudo socat stdio /run/haproxy/admin.sock ``` Otros estados útiles son `maint`, que lo saca por completo y detiene también sus comprobaciones, y `set server web_servers/web1 weight 50%` para reducir su carga temporalmente. Los cambios hechos por el socket solo viven en memoria: se pierden si reinicias o recargas HAProxy. > Consejo: el mismo efecto se consigue desde la aplicación. Si tu endpoint de salud devuelve `503` durante el apagado ordenado del servicio, HAProxy lo sacará solo de la rotación antes de que deje de atender. ## Paso 7: Ajustar la velocidad de detección Con `inter 2s fall 3` un servidor caído tarda unos 6 segundos en salir de la rotación. Para reaccionar antes sin multiplicar las comprobaciones normales, HAProxy permite intervalos distintos según el estado: ```haproxy default-server inter 3s fastinter 1s downinter 5s fall 3 rise 2 on-marked-down shutdown-sessions ``` - `fastinter 1s`: intervalo usado mientras un servidor está cambiando de estado (tras el primer fallo o el primer éxito). La caída se detecta en unos 3 segundos. - `downinter 5s`: intervalo para servidores ya caídos, para no insistir innecesariamente. Si además quieres validar el contenido de la respuesta y no solo el código, sustituye la línea `http-check expect` por `http-check expect string OK`, que exige que el cuerpo contenga ese texto. ## Paso 8: Comprobaciones para servicios que no son HTTP Para backends TCP (bases de datos, Redis, colas) usa `mode tcp` en el backend. HAProxy incluye comprobaciones que hablan el protocolo del servicio, más fiables que comprobar solo el puerto. Estos bloques son de referencia: adáptalos a tus direcciones y añádelos junto a un `frontend` en `mode tcp`. Redis, enviando `PING` y esperando `+PONG`: ```haproxy backend redis_servers mode tcp option tcp-check tcp-check send PING\r\n tcp-check expect string +PONG server redis1 10.0.0.31:6379 check inter 2s fall 3 rise 2 ``` PostgreSQL y MySQL o MariaDB, con un usuario dedicado sin privilegios que debes crear en la base de datos: ```haproxy backend pg_servers mode tcp option pgsql-check user haproxy_check server pg1 10.0.0.41:5432 check backend mysql_servers mode tcp option mysql-check user haproxy_check post-41 server db1 10.0.0.51:3306 check ``` Estas comprobaciones confirman que el servidor responde a nivel de protocolo, pero no si es el primario o una réplica. Para distinguirlo necesitas un endpoint HTTP auxiliar (por ejemplo, Patroni expone uno para PostgreSQL) y combinar `check port ` con `option httpchk`. ## Solución de problemas - **Todos los servidores en `DOWN` con `Layer4 connection problem`**: HAProxy no llega al puerto. Comprueba la dirección y el puerto con `curl -i http://:/health` desde el balanceador y revisa el cortafuegos del backend. - **`Layer7 wrong status, code: 404`**: la ruta de salud no existe o depende del dominio. Revisa `uri` y la cabecera `Host` de `http-check send`. - **`Layer7 timeout`**: el endpoint tarda más que el tiempo límite de la comprobación (por defecto el valor de `inter`, o `timeout check` si lo defines en `defaults`). Haz que `/health` sea rápido y no ejecute consultas pesadas. - **HAProxy no arranca tras editar `errorfile`**: el archivo debe empezar por la línea de estado HTTP y no superar el tamaño de un búfer (16 KB por defecto). Revisa `sudo haproxy -c -f /etc/haproxy/haproxy.cfg`. - **No ves cambios de estado en `/var/log/haproxy.log`**: comprueba que rsyslog está activo con `systemctl status rsyslog`; como alternativa, usa `sudo journalctl -u haproxy -f`. ## Conclusión HAProxy ya saca automáticamente de la rotación los servidores que fallan su comprobación de salud, usa un servidor de respaldo cuando caen los principales, muestra una página de mantenimiento si no queda ninguno y te permite drenar servidores para hacer mantenimiento sin cortes. Como siguientes pasos, añade terminación TLS al frontend, envía los cambios de estado a tu sistema de alertas y elimina el punto único de fallo del propio balanceador con un segundo nodo HAProxy y keepalived compartiendo una IP flotante.