Envoy es un proxy de capa 7 de alto rendimiento, proyecto graduado de la CNCF, que se usa como proxy de borde, balanceador de carga y sidecar en mallas de servicios como Istio. Se configura con un archivo YAML basado en cuatro piezas: listeners (dónde escucha), filtros (qué hace con el tráfico), rutas (a dónde lo envía) y clusters (los backends).
En este tutorial instalarás Envoy en Ubuntu 24.04 a partir del binario oficial, lo ejecutarás como servicio systemd con un usuario sin privilegios y lo configurarás como balanceador HTTP delante de dos backends, con health checks activos, reintentos y la interfaz de administración para comprobar su estado.
Requisitos previos
Para seguir este tutorial necesitas:
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 1 GB de RAM.
- Un usuario no root con privilegios
sudo. - El puerto 80 libre (si tienes Nginx o Apache escuchando en él, detenlo o usa otro puerto en el listener).
Paso 1: Descargar e instalar el binario de Envoy
El repositorio APT oficial de Envoy no publica paquetes para Ubuntu 24.04 y va varias versiones por detrás, así que la forma más fiable de instalarlo es el binario estático que se adjunta a cada release en GitHub. Consulta la última versión en la página de releases de Envoy y guárdala en una variable (este tutorial usa la 1.39.1):
ENVOY_VERSION=1.39.1
Descarga el binario y el archivo de checksums:
cd /tmp
curl -fLO "https://github.com/envoyproxy/envoy/releases/download/v${ENVOY_VERSION}/envoy-${ENVOY_VERSION}-linux-x86_64"
curl -fLO "https://github.com/envoyproxy/envoy/releases/download/v${ENVOY_VERSION}/checksums.txt.asc"
En un servidor ARM64, sustituye x86_64 por aarch_64 en el nombre del archivo.
Comprueba que el SHA-256 del binario coincide con el publicado:
echo "$(grep "/envoy-${ENVOY_VERSION}-linux-x86_64$" checksums.txt.asc | cut -d' ' -f1) envoy-${ENVOY_VERSION}-linux-x86_64" | sha256sum -c
envoy-1.39.1-linux-x86_64: OK
Instálalo en /usr/local/bin con los permisos correctos:
sudo install -m 0755 "envoy-${ENVOY_VERSION}-linux-x86_64" /usr/local/bin/envoy
envoy --version
envoy version: .../1.39.1/Clean/RELEASE/BoringSSL
NotaSi prefieres contenedores, la imagen oficial es
envoyproxy/envoy:v1.39-latest. El archivo de configuración de este tutorial funciona igual montándolo en/etc/envoy/envoy.yamldentro del contenedor.
Paso 2: Crear el usuario y el directorio de configuración
Envoy no necesita privilegios de root. Crea un usuario de sistema sin shell y el directorio donde vivirá la configuración:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin envoy
sudo mkdir -p /etc/envoy
Comprueba que el usuario existe:
id envoy
uid=998(envoy) gid=998(envoy) groups=998(envoy)
Paso 3: Levantar dos backends de prueba
Para ver el balanceo necesitas algo detrás de Envoy. Si ya tienes una aplicación escuchando en local, puedes saltarte este paso y usar sus puertos en el Paso 4. Si no, crea dos servidores HTTP mínimos con Python, cada uno con una página que indica quién responde:
sudo mkdir -p /srv/backend1 /srv/backend2
echo "respuesta desde backend1" | sudo tee /srv/backend1/index.html
echo "respuesta desde backend2" | sudo tee /srv/backend2/index.html
Arráncalos como servicios transitorios de systemd, escuchando solo en 127.0.0.1 en los puertos 8081 y 8082:
sudo systemd-run --unit=backend1 python3 -m http.server 8081 --bind 127.0.0.1 --directory /srv/backend1
sudo systemd-run --unit=backend2 python3 -m http.server 8082 --bind 127.0.0.1 --directory /srv/backend2
Comprueba que responden:
curl http://127.0.0.1:8081/
curl http://127.0.0.1:8082/
respuesta desde backend1
respuesta desde backend2
Paso 4: Escribir la configuración de Envoy
La configuración estática de Envoy tiene dos bloques principales. En static_resources.listeners defines el puerto en el que escucha y la cadena de filtros; para HTTP, el filtro clave es el http_connection_manager, que contiene las rutas y termina en el filtro router. En static_resources.clusters defines los backends, la política de balanceo y los health checks. El bloque admin expone la interfaz de administración.
Crea el archivo de configuración:
sudo nano /etc/envoy/envoy.yaml
Pega el siguiente contenido:
admin:
address:
socket_address: { address: 127.0.0.1, port_value: 9901 }
static_resources:
listeners:
- name: http_listener
address:
socket_address: { address: 0.0.0.0, port_value: 80 }
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
route_config:
name: local_route
virtual_hosts:
- name: app
domains: ["*"]
routes:
- match: { prefix: "/" }
route:
cluster: app_backend
timeout: 15s
retry_policy:
retry_on: "connect-failure,refused-stream,5xx"
num_retries: 2
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: app_backend
connect_timeout: 2s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: app_backend
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: 127.0.0.1, port_value: 8081 }
- endpoint:
address:
socket_address: { address: 127.0.0.1, port_value: 8082 }
health_checks:
- timeout: 1s
interval: 5s
unhealthy_threshold: 2
healthy_threshold: 2
http_health_check:
path: "/"
Qué hace cada parte:
admin: la interfaz de administración escucha solo en127.0.0.1:9901. Nunca la expongas a Internet: permite, entre otras cosas, apagar Envoy.- Listener
http_listener: acepta HTTP en el puerto 80 de todas las interfaces y escribe un log de acceso por petición en la salida estándar, que acabará en journald. route_config: todas las peticiones (domains: ["*"], prefijo/) van al clusterapp_backend, con un timeout de 15 segundos y hasta 2 reintentos si el backend rechaza la conexión o devuelve un 5xx.- Cluster
app_backend: dos endpoints con IP fija (type: STATIC) balanceados en round robin. El health check pide/cada 5 segundos y saca un endpoint del balanceo tras 2 fallos seguidos.
Si tus backends se identifican por nombre DNS en lugar de IP, usa type: STRICT_DNS y pon el nombre en address.
Valida la sintaxis antes de arrancar nada:
envoy --mode validate -c /etc/envoy/envoy.yaml
configuration '/etc/envoy/envoy.yaml' OK
Si hay un error, Envoy indica el campo exacto que no reconoce. Los fallos más habituales son la indentación y un @type mal escrito.
Paso 5: Ejecutar Envoy como servicio systemd
Crea una unidad de systemd que ejecute Envoy con el usuario envoy. La capacidad CAP_NET_BIND_SERVICE le permite escuchar en el puerto 80 sin ser root, y ExecStartPre valida la configuración antes de cada arranque para que un error de sintaxis no tumbe el servicio en marcha:
sudo nano /etc/systemd/system/envoy.service
[Unit]
Description=Envoy Proxy
After=network-online.target
Wants=network-online.target
[Service]
User=envoy
Group=envoy
ExecStartPre=/usr/local/bin/envoy --mode validate -c /etc/envoy/envoy.yaml
ExecStart=/usr/local/bin/envoy -c /etc/envoy/envoy.yaml --log-level info
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
LimitNOFILE=65536
Restart=on-failure
[Install]
WantedBy=multi-user.target
Recarga systemd, habilita el servicio y arráncalo:
sudo systemctl daemon-reload
sudo systemctl enable --now envoy
sudo systemctl status envoy
● envoy.service - Envoy Proxy
Loaded: loaded (/etc/systemd/system/envoy.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-09-25 10:12:41 UTC; 3s ago
Si usas UFW, abre el puerto 80:
sudo ufw allow 80/tcp
Paso 6: Comprobar el balanceo
Lanza varias peticiones seguidas. Envoy las reparte en round robin entre los dos backends:
for i in 1 2 3 4; do curl -s http://localhost/; done
respuesta desde backend1
respuesta desde backend2
respuesta desde backend1
respuesta desde backend2
Cada petición deja una línea en el log de acceso, que puedes seguir con journald:
sudo journalctl -u envoy -f
[2026-09-25T10:13:02.114Z] "GET / HTTP/1.1" 200 - 0 25 1 1 "-" "curl/8.5.0" "..." "localhost" "127.0.0.1:8081"
El último campo indica qué backend atendió la petición. Pulsa CTRL+C para salir.
Paso 7: Usar la interfaz de administración y probar los health checks
La interfaz de administración es la herramienta principal para saber qué está haciendo Envoy. Comprueba que el proceso está listo para recibir tráfico:
curl -s http://127.0.0.1:9901/ready
LIVE
Consulta el estado de salud de cada endpoint del cluster:
curl -s http://127.0.0.1:9901/clusters | grep health_flags
app_backend::127.0.0.1:8081::health_flags::healthy
app_backend::127.0.0.1:8082::health_flags::healthy
Ahora simula la caída de un backend:
sudo systemctl stop backend2
Espera unos 10 segundos (dos health checks fallidos) y vuelve a consultar:
curl -s http://127.0.0.1:9901/clusters | grep health_flags
app_backend::127.0.0.1:8081::health_flags::healthy
app_backend::127.0.0.1:8082::health_flags::/failed_active_hc
Todas las peticiones van ya a backend1, sin errores para el cliente:
for i in 1 2 3; do curl -s http://localhost/; done
respuesta desde backend1
respuesta desde backend1
respuesta desde backend1
Las estadísticas del cluster muestran cuántas peticiones se han servido y cuántos health checks han fallado:
curl -s http://127.0.0.1:9901/stats | grep -E 'cluster.app_backend.(upstream_rq_2xx|health_check.failure)'
cluster.app_backend.health_check.failure: 4
cluster.app_backend.upstream_rq_2xx: 7
Otros endpoints útiles son /config_dump (la configuración efectiva en JSON), /listeners y /stats/prometheus (las mismas métricas en formato Prometheus, listas para que las recoja tu sistema de monitorización a través de un túnel o un proxy autenticado).
Cuando termines las pruebas, detén y elimina los backends de ejemplo:
sudo systemctl stop backend1
sudo rm -rf /srv/backend1 /srv/backend2
Aplicar cambios de configuración
Envoy no relee envoy.yaml al recibir una señal. Para aplicar cambios en una configuración estática, valida y reinicia:
envoy --mode validate -c /etc/envoy/envoy.yaml && sudo systemctl restart envoy
El reinicio corta las conexiones abiertas durante un instante. Si necesitas cambios sin cortes, el camino es la configuración dinámica (xDS) con un plano de control, que es lo que hacen Istio o Envoy Gateway.
Solución de problemas
El servicio no arranca y journalctl -u envoy muestra un error de validación: el ExecStartPre ha detectado un error en el YAML. Ejecuta envoy --mode validate -c /etc/envoy/envoy.yaml para ver el campo exacto.
cannot bind '0.0.0.0:80': Address already in use: otro proceso usa el puerto 80. Identifícalo con sudo ss -ltnp 'sport = :80' y detenlo o cambia port_value en el listener.
cannot bind '0.0.0.0:80': Permission denied: falta la capacidad CAP_NET_BIND_SERVICE en la unidad. Revisa las líneas AmbientCapabilities y CapabilityBoundingSet y ejecuta sudo systemctl daemon-reload.
Envoy responde 503 con no healthy upstream: todos los endpoints han fallado el health check. Comprueba con /clusters qué endpoints están marcados como /failed_active_hc y que la ruta del http_health_check devuelve un 200 en tus backends.
Envoy responde 503 con upstream connect error: el backend no acepta conexiones en la IP y puerto configurados. Pruébalo directamente con curl desde el servidor.
Conclusión
Tienes Envoy instalado en Ubuntu 24.04 como servicio systemd sin privilegios de root, balanceando tráfico HTTP entre dos backends con health checks activos y reintentos, y sabes usar la interfaz de administración para ver el estado de cada endpoint y las métricas.
Como siguientes pasos puedes añadir un listener en el puerto 443 con terminación TLS mediante un transport_socket y tus certificados, crear rutas por prefijo o por dominio que apunten a clusters distintos, o recoger /stats/prometheus con Prometheus para vigilar latencias y errores.
