// 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:

  1. HTTPS. El proxy acepta conexiones cifradas y almacena el certificado, mientras n8n funciona internamente en el servidor por HTTP normal.
  2. 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.
  3. 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

  1. Actualice paquetes:

    bash
    sudo apt update && sudo apt upgrade -y
  2. Compruebe Docker:

    bash
    docker --version
    docker compose version

    Si los comandos no se encuentran, instale Docker según la guía oficial para su sistema.

  3. Cree el directorio del proyecto y subdirectorios:

    bash
    mkdir -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.

env
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-32

Genere 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.

yaml
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_USER y N8N_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 PGDATA es necesaria para que Postgres 18 almacene los datos en el volumen montado.
  • WEBHOOK_URL y N8N_PROTOCOL. Sin ellas n8n mostrará URLs de webhooks tipo http://localhost:5678/..., y los servicios externos no podrán llegar a ellos.
  • N8N_PROXY_HOPS=1 indica a n8n que hay un proxy delante y que debe obtener la dirección del cliente desde el encabezado X-Forwarded-For.
  • El puerto 127.0.0.1:5678 no 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:

ini
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 check
  • option forwardfor añade el encabezado X-Forwarded-For con la dirección del cliente.
  • timeout tunnel 1h es 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 en docker-compose.yml y una comprobación de disponibilidad.

Inicie los contenedores y compruebe su estado:

bash
cd ~/n8n-project
docker compose up -d
docker compose ps

Paso 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.

  1. Instale acme.sh y socat (socat es necesario para el modo standalone):

    bash
    sudo apt install -y socat
    curl https://get.acme.sh | sh -s email=admin@example.com
    source ~/.bashrc
  2. Elija 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 letsencrypt
  3. Emita 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"
  4. Instale el certificado para HAProxy. HAProxy espera la clave y la cadena de certificados en un único archivo. El comando de --reloadcmd monta 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:

ini
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 check

Compruebe la sintaxis y aplique la configuración:

bash
cd ~/n8n-project
docker compose run --rm haproxy haproxy -c -f /usr/local/etc/haproxy/haproxy.cfg
docker compose restart haproxy

Paso 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:
    bash
    docker 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 volumen n8n-data y 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

// Contact

¿Necesitas ayuda?

Escríbeme y te ayudaré a resolver el problema

Respondo en un día laborable (03:00-13:00 GMT)

Или оставьте заявку здесь:

Confirme que no es un bot.

Escribir y recibir una respuesta rápida