--- title: "Balanceo de carga con HAProxy en Ubuntu 24.04 con HTTPS" description: "Configura HAProxy en Ubuntu 24.04 como balanceador de carga con HTTPS de Let's Encrypt, health checks, límite de peticiones por IP y página de estadísticas." category: "Temas Avanzados" language: es source: https://cubepath.com/es/docs/temas-avanzados/balanceo-de-carga-con-haproxy updated: 2026-09-25 --- # Balanceo de carga con HAProxy en Ubuntu 24.04 con HTTPS HAProxy es un balanceador de carga y proxy inverso de alto rendimiento para TCP y HTTP. Reparte las peticiones entre varios servidores, deja de enviar tráfico a los que fallan y puede terminar TLS para que los servidores de aplicación no tengan que hacerlo. En esta guía instalarás HAProxy en Ubuntu 24.04, lo pondrás delante de dos servidores web con comprobaciones de salud, añadirás HTTPS con Let's Encrypt, limitarás las peticiones por IP y activarás la página de estadísticas. ## Requisitos previos Necesitas: - Un servidor con Ubuntu 24.04 LTS para HAProxy, por ejemplo un VPS de CubePath, con un usuario no root con privilegios `sudo`. - Dos servidores web (Nginx, Apache o tu aplicación) que respondan por HTTP en el puerto 80 en una red privada. En esta guía se usan `10.0.0.11` y `10.0.0.12`. - Un dominio con un registro DNS A que apunte a la IP pública del balanceador. Se usará `your_domain` como marcador. - Los puertos 80 y 443 accesibles desde Internet en el balanceador. ## Paso 1: Instalar HAProxy Ubuntu 24.04 incluye HAProxy 2.8, una versión LTS con soporte a largo plazo, suficiente para todo lo que se ve en esta guía: ```bash sudo apt update sudo apt install haproxy ``` Comprueba la versión y que el servicio está activo: ```bash haproxy -v | head -n 1 systemctl status haproxy --no-pager ``` ```output HAProxy version 2.8.x-1ubuntu3 - https://haproxy.org/ ... Active: active (running) ``` Abre los puertos web en el cortafuegos: ```bash sudo ufw allow 80/tcp sudo ufw allow 443/tcp ``` > Nota: si necesitas una rama más reciente (3.x), el mantenedor del paquete de Debian publica un PPA en `ppa:vbernat/haproxy-3.2`. La configuración de esta guía funciona igual en ambas versiones. ## Paso 2: Preparar los servidores web Cada servidor web debe responder a una ruta de comprobación de salud. Lo más sencillo es un archivo estático `/health`. En cada servidor web con Nginx: ```bash echo "ok" | sudo tee /var/www/html/health hostname | sudo tee /var/www/html/index.html ``` La segunda línea hace que cada servidor devuelva su nombre, lo que te permitirá ver el reparto de carga más adelante. Desde el balanceador, confirma que llegas a ambos: ```bash curl http://10.0.0.11/health curl http://10.0.0.12/health ``` ```output ok ok ``` ## Paso 3: Configurar el balanceo HTTP El archivo de configuración es `/etc/haproxy/haproxy.cfg`. Guarda una copia del original y ábrelo: ```bash sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.orig sudo nano /etc/haproxy/haproxy.cfg ``` Deja la sección `global` que trae Ubuntu (define el usuario, el `chroot`, el socket de administración en `/run/haproxy/admin.sock` y los parámetros TLS por defecto) y sustituye todo lo que va desde `defaults` hasta el final por lo siguiente: ```haproxy defaults log global mode http option httplog option dontlognull option forwardfor timeout connect 5s timeout client 30s timeout server 30s timeout http-request 10s errorfile 400 /etc/haproxy/errors/400.http errorfile 403 /etc/haproxy/errors/403.http errorfile 408 /etc/haproxy/errors/408.http errorfile 500 /etc/haproxy/errors/500.http errorfile 502 /etc/haproxy/errors/502.http errorfile 503 /etc/haproxy/errors/503.http errorfile 504 /etc/haproxy/errors/504.http frontend http_in bind :80 acl acme path_beg /.well-known/acme-challenge/ use_backend letsencrypt if acme default_backend web_servers backend web_servers balance roundrobin option httpchk http-check send meth GET uri /health http-check expect status 200 default-server inter 3s fall 3 rise 2 server web1 10.0.0.11:80 check server web2 10.0.0.12:80 check backend letsencrypt server certbot 127.0.0.1:8888 ``` Qué hace cada parte: - `option forwardfor` añade la cabecera `X-Forwarded-For` con la IP real del cliente. - `http-check` pide `/health` a cada servidor cada 3 segundos (`inter 3s`). Tras 3 fallos seguidos (`fall 3`) el servidor sale del reparto y vuelve tras 2 respuestas correctas (`rise 2`). - `balance roundrobin` reparte las peticiones por turnos. Para conexiones largas (WebSockets, bases de datos) suele ir mejor `leastconn`, y si necesitas que un cliente vaya siempre al mismo servidor sin cookies, `source`. - El backend `letsencrypt` servirá para obtener el certificado en el siguiente paso. De momento no hay nada escuchando en el puerto 8888, lo cual es normal. Valida la sintaxis antes de aplicar cualquier cambio: ```bash sudo haproxy -c -f /etc/haproxy/haproxy.cfg ``` ```output Configuration file is valid ``` Recarga HAProxy. `reload` aplica la nueva configuración sin cortar las conexiones abiertas: ```bash sudo systemctl reload haproxy ``` Haz varias peticiones seguidas y verás cómo se alternan los servidores (cada uno responde con su nombre de host, aquí `web1` y `web2`): ```bash for i in 1 2 3 4; do curl -s http://your_domain; done ``` ```output web1 web2 web1 web2 ``` ## Paso 4: Obtener un certificado de Let's Encrypt HAProxy ya ocupa el puerto 80, así que Certbot se ejecutará en modo `standalone` escuchando en el puerto 8888, y HAProxy le reenviará las peticiones de validación gracias a la ACL `acme` del paso anterior. Instala Certbot: ```bash sudo apt install certbot ``` Solicita el certificado, sustituyendo `your_domain` y tu correo: ```bash sudo certbot certonly --standalone --http-01-port 8888 \ -d your_domain --agree-tos -m you@example.com --no-eff-email ``` ```output Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/your_domain/fullchain.pem Key is saved at: /etc/letsencrypt/live/your_domain/privkey.pem ``` HAProxy espera la cadena de certificados y la clave privada en un único archivo PEM. Crea el directorio y combina ambos: ```bash sudo mkdir -p /etc/haproxy/certs sudo sh -c 'cat /etc/letsencrypt/live/your_domain/fullchain.pem /etc/letsencrypt/live/your_domain/privkey.pem > /etc/haproxy/certs/your_domain.pem' sudo chmod 600 /etc/haproxy/certs/your_domain.pem ``` Para que el archivo combinado se regenere en cada renovación, crea un *deploy hook* de Certbot: ```bash sudo nano /etc/letsencrypt/renewal-hooks/deploy/haproxy.sh ``` ```bash #!/usr/bin/env bash set -euo pipefail domain="$(basename "$RENEWED_LINEAGE")" cat "$RENEWED_LINEAGE/fullchain.pem" "$RENEWED_LINEAGE/privkey.pem" > "/etc/haproxy/certs/${domain}.pem" chmod 600 "/etc/haproxy/certs/${domain}.pem" systemctl reload haproxy ``` Hazlo ejecutable y prueba la renovación en modo simulación: ```bash sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/haproxy.sh sudo certbot renew --dry-run ``` ```output Congratulations, all simulated renewals succeeded: ``` El paquete de Certbot instala un temporizador de systemd (`certbot.timer`) que intenta la renovación dos veces al día. ## Paso 5: Activar HTTPS y redirigir HTTP Edita de nuevo `/etc/haproxy/haproxy.cfg`: ```bash sudo nano /etc/haproxy/haproxy.cfg ``` Sustituye el `frontend http_in` por esta versión, que sigue atendiendo las validaciones de Let's Encrypt pero redirige el resto a HTTPS, y añade el nuevo `frontend https_in`: ```haproxy frontend http_in bind :80 acl acme path_beg /.well-known/acme-challenge/ use_backend letsencrypt if acme http-request redirect scheme https code 301 unless acme frontend https_in bind :443 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1 http-request set-header X-Forwarded-Proto https http-response set-header Strict-Transport-Security "max-age=31536000" default_backend web_servers ``` `crt /etc/haproxy/certs/` carga todos los PEM de ese directorio y elige el correcto según el nombre solicitado (SNI), así que podrás añadir más dominios sin tocar la configuración. `alpn h2,http/1.1` habilita HTTP/2 con los clientes. Los servidores web siguen recibiendo HTTP en claro por la red privada, con la cabecera `X-Forwarded-Proto` para que la aplicación sepa que el cliente usó HTTPS. Valida y recarga: ```bash sudo haproxy -c -f /etc/haproxy/haproxy.cfg sudo systemctl reload haproxy ``` Comprueba la redirección y la respuesta por HTTPS: ```bash curl -sI http://your_domain | head -n 2 curl -s https://your_domain ``` ```output HTTP/1.1 301 Moved Permanently content-length: 0 web1 ``` ## Paso 6: Limitar las peticiones por IP Una *stick table* permite a HAProxy llevar la cuenta de peticiones por IP y rechazar a quien supere un umbral. Añade estas líneas al `frontend https_in`, justo antes de `default_backend`: ```haproxy stick-table type ip size 100k expire 30s store http_req_rate(10s) http-request track-sc0 src http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 } ``` Con esto, una IP que haga más de 100 peticiones en 10 segundos recibirá un `429 Too Many Requests` hasta que baje de ese ritmo. Ajusta el umbral a tu tráfico real: una página con muchos recursos puede generar decenas de peticiones en una sola visita. Valida, recarga y, si tienes instalado `ab` (paquete `apache2-utils`), compruébalo desde otro equipo: ```bash ab -n 300 -c 20 https://your_domain/ ``` En el resultado, las respuestas rechazadas aparecen en `Non-2xx responses`. ## Paso 7: Activar la página de estadísticas HAProxy incluye una página web con el estado de cada frontend, backend y servidor. Añade al final de `/etc/haproxy/haproxy.cfg` un frontend dedicado, con una contraseña propia en lugar de `your_strong_password`: ```haproxy frontend stats bind :8404 stats enable stats uri /stats stats refresh 10s stats auth admin:your_strong_password ``` Valida, recarga y permite el acceso al puerto 8404 solo desde tu IP de administración: ```bash sudo haproxy -c -f /etc/haproxy/haproxy.cfg sudo systemctl reload haproxy sudo ufw allow from your_admin_ip to any port 8404 proto tcp ``` Abre `http://your_domain:8404/stats` en el navegador. Los servidores sanos aparecen en verde con el estado `UP`. También puedes consultar y controlar HAProxy desde la terminal a través de su socket de administración con `socat`: ```bash sudo apt install socat echo "show servers state web_servers" | sudo socat stdio /run/haproxy/admin.sock ``` Para sacar un servidor del reparto antes de un mantenimiento sin cortar las conexiones activas, ponlo en modo `drain` y devuélvelo con `ready` cuando termines: ```bash echo "set server web_servers/web1 state drain" | sudo socat stdio /run/haproxy/admin.sock echo "set server web_servers/web1 state ready" | sudo socat stdio /run/haproxy/admin.sock ``` Estos cambios se pierden al reiniciar HAProxy; son para operaciones puntuales. ## Paso 8: Revisar los registros En Ubuntu, HAProxy envía sus logs a través de rsyslog al archivo `/var/log/haproxy.log`. Cada línea incluye el cliente, el frontend, el backend y el servidor que atendió la petición, los tiempos y el código de respuesta: ```bash sudo tail -f /var/log/haproxy.log ``` ```output ... https_in~ web_servers/web2 0/0/1/2/3 200 312 - - ---- 3/3/0/0/0 0/0 "GET / HTTP/2.0" ``` Los cinco números separados por barras son, en milisegundos, el tiempo de recepción de la petición, la espera en cola, la conexión con el servidor, la respuesta del servidor y el total. Si el cuarto valor es alto, el cuello de botella está en tu aplicación, no en HAProxy. ## Solución de problemas **Un servidor aparece como `DOWN`.** Reproduce la comprobación de salud desde el balanceador con `curl -i http://10.0.0.11/health`. Si no devuelve `200`, el problema está en el servidor web o en la ruta. Los cambios de estado quedan registrados en `/var/log/haproxy.log` con el motivo (`Layer4 connection problem`, `Layer7 wrong status`). **HAProxy no arranca tras un cambio.** Ejecuta siempre `sudo haproxy -c -f /etc/haproxy/haproxy.cfg` antes de recargar; indica la línea exacta del error. Consulta también `sudo journalctl -u haproxy -n 50`. **Error `unable to load SSL certificate`.** El archivo PEM no existe, está vacío o le falta la clave privada. Comprueba que contiene ambos bloques con `sudo grep -c BEGIN /etc/haproxy/certs/your_domain.pem` (debe devolver al menos 2). **La renovación de Certbot falla.** Comprueba que la ACL `acme` sigue en el frontend del puerto 80 y que la redirección a HTTPS lleva `unless acme`. **Error 503 `No server is available`.** Todos los servidores del backend están caídos. Revisa la página de estadísticas y los logs de los servidores web. ## Conclusión Tienes HAProxy repartiendo tráfico entre dos servidores web, retirando automáticamente los que fallan, terminando HTTPS con renovación automática del certificado y limitando el abuso por IP. Para eliminar el balanceador como punto único de fallo, el siguiente paso natural es montar un segundo nodo de HAProxy con Keepalived y una IP virtual. También puedes afinar los parámetros TCP del kernel con sysctl si esperas un volumen alto de conexiones.