// DevOps
Instalando n8n en Docker con HAProxy: Guía clara para principiantes
Publicado el 22.09.2026
En esta guía desplegamos n8n en Docker y lo publicamos hacia el exterior mediante HAProxy: un proxy inverso que acepta HTTPS y reenvía las solicitudes a n8n. La instrucción está pensada para principiantes: cada paso está explicado. Los comandos y parámetros se han comprobado con la documentación de n8n para la rama 2.x (en septiembre de 2026 vigente la 2.40).
Por qué usar HAProxy
Abrir el puerto de n8n directamente en Internet es posible, pero no es seguro ni cómodo. HAProxy resuelve tres tareas:
- HTTPS. El proxy acepta conexiones cifradas y almacena el certificado, mientras n8n funciona internamente en el servidor por HTTP normal.
- Punto de entrada único. Todas las solicitudes al dominio llegan a HAProxy, que decide a qué aplicación reenviarlas. En el mismo servidor se podrán añadir otros servicios más adelante.
- Flexibilidad. Las reglas de enrutamiento, restricciones y balanceo se pueden añadir después sin tocar n8n. Más sobre HAProxy en el artículo Servidores proxy: Parte 3 — HAProxy.
Qué necesitará
- Servidor con Ubuntu o Debian. Para una instalación pequeña bastan 2 núcleos y 2–4 GB de memoria; la elección entre la versión cloud de n8n y su propio servidor se trata en el artículo n8n: Parte 2 — Versión en la nube o servidor propio.
- Docker y Docker Compose (el plugin
docker compose). - Nombre de dominio con un registro A apuntando a la IP del servidor.
- Acceso SSH con permisos
sudo.
Paso 1: Preparación del servidor y estructura del proyecto
Actualice paquetes:
bashsudo apt update && sudo apt upgrade -yCompruebe Docker:
bashdocker --version docker compose versionSi los comandos no se encuentran, instale Docker según la guía oficial para su sistema.
Cree el directorio del proyecto y subdirectorios:
bashmkdir -p ~/n8n-project/haproxy/certs cd ~/n8n-project
Paso 2: Archivo .env con secretos
En el directorio ~/n8n-project cree el archivo .env. Docker Compose sustituirá desde él los valores en docker-compose.yml.
N8N_DOMAIN=n8n.example.com
POSTGRES_USER=n8n
POSTGRES_PASSWORD=reemplace-por-una-contraseña-larga
POSTGRES_DB=n8n
# clave de cifrado para las credenciales en n8n: genere una vez y guárdela
N8N_ENCRYPTION_KEY=reemplace-por-el-resultado-de-openssl-rand-hex-32Genere la clave de cifrado con el comando openssl rand -hex 32. Con esa clave n8n cifra las credenciales guardadas: si se pierde, no será posible recuperarlas desde una copia de seguridad. Guarde la clave aparte del servidor.
Paso 3: Archivo docker-compose.yml
Cree ~/n8n-project/docker-compose.yml. La línea version: al principio del archivo ya no es necesaria: Docker Compose moderno la ignora.
services:
postgres:
image: postgres:18
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
PGDATA: /var/lib/postgresql/data
volumes:
- db-storage:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
n8n:
image: n8nio/n8n:2.40.5 # fije la versión y actualícela con cuidado
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678" # el puerto solo está disponible dentro del servidor
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: "5432"
DB_POSTGRESDB_DATABASE: ${POSTGRES_DB}
DB_POSTGRESDB_USER: ${POSTGRES_USER}
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
N8N_HOST: ${N8N_DOMAIN}
N8N_PORT: "5678"
N8N_PROTOCOL: https
WEBHOOK_URL: https://${N8N_DOMAIN}/
N8N_PROXY_HOPS: "1" # n8n está detrás de un proxy inverso
NODE_ENV: production
volumes:
- n8n-data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
haproxy:
image: haproxy:3.2 # rama con soporte a largo plazo
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./haproxy:/usr/local/etc/haproxy:ro
depends_on:
- n8n
volumes:
db-storage:
n8n-data:Qué es importante aquí:
- No poner usuario ni contraseña en variables de acceso. Antes en este tipo de instrucciones se usaban
N8N_BASIC_AUTH_USERyN8N_BASIC_AUTH_PASSWORD, pero el soporte para basic auth se eliminó en n8n desde la versión 1.0. El acceso ahora es solo mediante la cuenta del propietario, que creará al abrir n8n por primera vez. - PostgreSQL en lugar de SQLite. Para funcionamiento persistente la documentación de n8n recomienda PostgreSQL; la variable
PGDATAes necesaria para que Postgres 18 almacene los datos en el volumen montado. WEBHOOK_URLyN8N_PROTOCOL. Sin ellas n8n mostrará URLs de webhooks tipohttp://localhost:5678/..., y los servicios externos no podrán llegar a ellos.N8N_PROXY_HOPS=1indica a n8n que hay un proxy delante y que debe obtener la dirección del cliente desde el encabezadoX-Forwarded-For.- El puerto
127.0.0.1:5678no es visible desde Internet: solo se puede acceder a n8n a través de HAProxy.
Paso 4: HAProxy para HTTP
Primero arrancamos todo por HTTP: aún no hay certificado. Cree ~/n8n-project/haproxy/haproxy.cfg:
global
log stdout format raw local0
defaults
log global
mode http
option httplog
option forwardfor
timeout connect 5s
timeout client 50s
timeout server 50s
timeout tunnel 1h
frontend http_front
bind :80
acl host_n8n hdr(host) -i n8n.example.com
use_backend n8n_backend if host_n8n
backend n8n_backend
server n8n n8n:5678 checkoption forwardforañade el encabezadoX-Forwarded-Forcon la dirección del cliente.timeout tunnel 1hes necesario para WebSocket: el editor de n8n mantiene una conexión persistente con el servidor, y sin este parámetro HAProxy la cortaría tras 50 segundos.server n8n n8n:5678 check— la dirección del contenedor por el nombre del servicio endocker-compose.ymly una comprobación de disponibilidad.
Inicie los contenedores y compruebe su estado:
cd ~/n8n-project
docker compose up -d
docker compose psPaso 5: Certificado Let’s Encrypt con acme.sh
HAProxy no sirve archivos desde disco, por lo que la comprobación del dominio es más sencilla en modo standalone: acme.sh levanta temporalmente un servidor web en el puerto 80. Para que el puerto esté libre, durante la emisión y renovación HAProxy se detiene temporalmente — esto lo gestionan los hooks --pre-hook y --post-hook que acme.sh recuerda y ejecuta en cada renovación.
Instale acme.sh y socat (socat es necesario para el modo standalone):
bashsudo apt install -y socat curl https://get.acme.sh | sh -s email=admin@example.com source ~/.bashrcElija la autoridad certificadora. Desde la versión 3.0 acme.sh usa ZeroSSL por defecto; si quiere Let’s Encrypt, indíqueselo explícitamente:
bash~/.acme.sh/acme.sh --set-default-ca --server letsencryptEmita el certificado:
bash~/.acme.sh/acme.sh --issue --standalone -d n8n.example.com \ --pre-hook "docker compose -f $HOME/n8n-project/docker-compose.yml stop haproxy" \ --post-hook "docker compose -f $HOME/n8n-project/docker-compose.yml start haproxy"Instale el certificado para HAProxy. HAProxy espera la clave y la cadena de certificados en un único archivo. El comando de
--reloadcmdmonta ese archivo y reinicia HAProxy en cada renovación:bash~/.acme.sh/acme.sh --install-cert -d n8n.example.com \ --key-file ~/n8n-project/haproxy/certs/privkey.pem \ --fullchain-file ~/n8n-project/haproxy/certs/fullchain.pem \ --reloadcmd "cat ~/n8n-project/haproxy/certs/fullchain.pem ~/n8n-project/haproxy/certs/privkey.pem > ~/n8n-project/haproxy/certs/n8n.pem && docker compose -f ~/n8n-project/docker-compose.yml restart haproxy"
La renovación la gestiona una tarea cron que acme.sh añadió al instalarse; puede comprobarla con crontab -l.
Paso 6: Activar HTTPS en HAProxy
Actualice haproxy.cfg a este contenido:
global
log stdout format raw local0
ssl-default-bind-options ssl-min-ver TLSv1.2
defaults
log global
mode http
option httplog
option forwardfor
timeout connect 5s
timeout client 50s
timeout server 50s
timeout tunnel 1h
frontend http_https_front
bind :80
bind :443 ssl crt /usr/local/etc/haproxy/certs/n8n.pem alpn h2,http/1.1
# redirigir a HTTPS a todos los que acceden por HTTP
http-request redirect scheme https code 301 unless { ssl_fc }
acl host_n8n hdr(host) -i n8n.example.com
use_backend n8n_backend if host_n8n
backend n8n_backend
http-request set-header X-Forwarded-Proto https if { ssl_fc }
server n8n n8n:5678 checkCompruebe la sintaxis y aplique la configuración:
cd ~/n8n-project
docker compose run --rm haproxy haproxy -c -f /usr/local/etc/haproxy/haproxy.cfg
docker compose restart haproxyPaso 7: Comprobación
Abra https://n8n.example.com. Debería ver el candado en la barra de direcciones y el asistente de creación de la cuenta del propietario — complételo de inmediato, antes de que terceros descubran la dirección. Tras iniciar sesión, abra cualquier nodo Webhook: la dirección del webhook debe empezar por https://n8n.example.com/.
Mantenimiento
- Actualizar n8n. Lea las notas de la nueva versión, haga una copia de seguridad y cambie la etiqueta de la imagen en
docker-compose.yml, luego:bashdocker compose pull n8n docker compose up -d - Copias de seguridad. Necesita tres cosas: el volcado de la base de datos (
docker compose exec postgres pg_dump -U n8n n8n > n8n.sql), el volumenn8n-datay la clave de cifrado del.env. - Certificado. Se renueva automáticamente; si hay fallos consulte el registro
~/.acme.sh/acme.sh.log. - Aumento de carga. Cuando haya muchos procesos, n8n se pone en modo de cola con workers independientes — sobre esto hay un artículo n8n: Parte 5 — Escalado: modo de cola, workers y task runners.
La misma instalación sin HAProxy, en el panel FastPanel, está descrita en el artículo Instalamos n8n en FastPanel con Docker Compose: Guía clara.
// Reviews
Reseñas relacionadas
Como siempre, rápido y de calidad. Para asuntos con los servidores, me dirijo a Mijaíl.
¡Como siempre, rápido y de calidad! Para asuntos relacionados con los servidores, me dirijo a Mijaíl.
// Contact
¿Necesitas ayuda?
Escríbeme y te ayudaré a resolver el problema
Respondo en un día laborable (03:00-13:00 GMT)
Или оставьте заявку здесь:
// Related