Todos dicen que montar n8n es fácil. Y sí: levantar el contenedor lo es. Lo difícil es que no se convierta en una ruleta rusa cuando ya está moviendo tickets, facturas o alertas reales.
Aquí tienes una guía por pasos, de menos a más, para desplegar n8n con reverse proxy, TLS, autenticación razonable, webhooks controlados, redes segmentadas, secretos bien guardados y un poco de hardening.

Todos dicen que es fácil. Mienten, punto.
Cuando se monta un sistema de este tipo, preguntando a GPT y sin saber muy bien lo que se hace se suele dar algo así: un servidor (VPS, ONPREM,..), Docker, puerto 5678 abierto a Internet, contraseña “fuerte” y un par de workflows que “solo” sincronizaban cosas.
Dos semanas después, ya estaba metido en el proceso de facturación (sin que nadie lo llamara así, claro). Algún día ese servidor se verá comprometido, se caerá o se borrara, entonces te darás un golpe de verdad: era infraestructura… pero sin prácticas de infraestructura.
Si vienes de IT/DevOps, esto te sonará: lo que empieza como prueba termina en crítico. Y n8n, por diseño, es una herramienta de integración con permisos potentes: toca APIs, credenciales, datos, y puede recibir tráfico entrante por webhooks. Si lo expones “a pelo”, estás invitando a que alguien lo use como trampolín.
Vamos a construir una configuración decente por capas. Cada paso añade seguridad.
Paso 0 — Inventario mínimo y modelo de amenaza
Antes de tocar Docker, decide esto:
- ¿Qué debe ser público? Normalmente: solo webhooks concretos. La UI de n8n (editor/admin) debería ser privada.
- ¿Quién entra? Usuarios internos (IT/ops) vs integraciones externas (SaaS, partners, formularios).
- ¿Qué se va al carajo si cae? Si mueve tickets, pagos o notificaciones críticas, ya no es una demo.
Esto te define el perímetro:
- UI privada: detrás de VPN, allowlist corporativa o red interna.
- Webhooks públicos: sí, pero con HTTPS, tokens/firmas, rate limiting y rutas dedicadas.
Y un recordatorio: si tu organización exige controles tipo ISO 27001, esto encaja perfecto en “medidas técnicas” (sin volverte loco). Si te interesa el marco, tengo un repaso en Iberasync: Sistema de gestión de la seguridad. La familia de las 27000.
Paso 1 — No publiques 5678: red interna + persistencia + base decente
El puerto 5678 es la interfaz HTTP de n8n, tu igual no lo sabias hasta que lo montas, pero todo cristo lo sabe. Publicarlo directo en Internet es el equivalente a dejar el panel de control de tu automatización en la calle. Aunque pongas password, sigues sin TLS, sin rate limit, sin WAF/controles, sin separación de UI vs webhooks… y con más superficie de ataque de la que necesitas.
Objetivo del paso: n8n y Postgres viven en una red interna Docker. El único que publica puertos es el reverse proxy (Paso 2).
Ejemplo de compose.yaml (esto son ejemplos, para hacer algo serio piensa bien cada línea):
services:
postgres:
image: postgres:16
environment:
POSTGRES_DB: n8n
POSTGRES_USER: n8n
POSTGRES_PASSWORD_FILE: /run/secrets/pg_password
secrets:
- pg_password
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- internal
# Ojo: NO ports:
n8n:
image: n8nio/n8n:1.XX.X # fija versión, no uses latest
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD_FILE: /run/secrets/pg_password
N8N_ENCRYPTION_KEY_FILE: /run/secrets/n8n_encryption_key
N8N_HOST: n8n.tu-dominio.com
N8N_PROTOCOL: https
# Verifica en la doc de tu versión el nombre exacto de estas vars
secrets:
- pg_password
- n8n_encryption_key
volumes:
- n8n_data:/home/node/.n8n
networks:
- internal
networks:
internal:
internal: true
volumes:
pgdata:
n8n_data:
secrets:
pg_password:
file: ./secrets/pg_password.txt
n8n_encryption_key:
file: ./secrets/n8n_encryption_key.txtPor qué así:
- Postgres no se publica: Nadie llega a tu BD, salvo la red interna de docker.
- Red interna: reduce exposición accidental.
- Versión fijada: upgrades planificados.
Si necesitas acceso local temporal (debug), publica con bind a loopback:
# Solo para mantenimiento local, no para producción expuesta ports: - "127.0.0.1:5678:5678"
Eso te obliga a entrar por SSH/VPN para verlo. Una molestia sana.
Paso 2 — Reverse proxy con TLS (y headers bien puestos)
Aquí es donde n8n empieza a parecer un servicio serio: un reverse proxy (Nginx/Traefik/Caddy) termina TLS, usa cabeceras correctas, aplica límites y te permite añadir autenticación delante.
Objetivo del paso: exponer solo 443 al exterior. Nada de 5678 abierto.
Ejemplo mínimo con Nginx (muy recortado):
server {
listen 443 ssl http2;
server_name n8n.tu-dominio.com;
# TLS: usa tu receta (ACME, cert corporativo, etc.)
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
# Cabeceras para que n8n sepa que va detrás de HTTPS
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location / {
proxy_pass http://n8n:5678;
}
}N8n necesita saber el esquema/host real para construir URLs (webhooks, callbacks OAuth, etc.). Si el proxy no pone X-Forwarded-*, luego vienen los me redirige a http y el festival de cookies mal puestas.
Ojo con los saltos de proxy: si tienes Cloud LB → WAF → Nginx → n8n, revisa la variable N8N_PROXY_HOPS (o equivalente) en tu versión. No voy a inventarme el número: verifica en la doc de tu versión cómo calcula n8n la IP real del cliente y qué cabeceras confía. Si te equivocas, tu rate limiting por IP no sirve para nada.
Si quieres repasar TLS sin humo (y con vocabulario de sistemas), tengo esto: Criptografía y criptoanálisis, TLS y seguridad en internet.

Paso 3 — Autenticación: lo recomendado en empresa
Vamos a separar la paja de la realidad de una empresa:
- SSO (SAML/OIDC): suele estar disponible en Enterprise. Verifica en la doc/licencia de tu versión. Normalmente no dispondrás de esto, la mayoría de la gente no paga, y si estas pagando no se que haces en este blog.
- El resto del mundo: necesita una capa adicional delante (proxy) + control de red (VPN/allowlist). Idealmente un firewall en condiciones y un WAF.
Mi enfoque recomendado para equipos IT/DevOps en empresa:
- UI de n8n (editor/admin): accesible solo desde VPN o allowlist corporativa. Y si además metes auth en el proxy, mejor.
- Webhooks: públicos solo si hace falta, pero con tokens/firmas y rate limiting (Paso 4).
Ejemplo rápido: Basic Auth en Nginx para la UI (no para webhooks públicos):
# Dentro del server {...}
location / {
auth_basic "n8n";
auth_basic_user_file /etc/nginx/htpasswd/n8n;
proxy_pass http://n8n:5678;
}¿Es “lo más moderno”? No. ¿Funciona? Sí, y reduce exposición. Si tu organización tiene IdP y quieres algo más fino, mira forward auth (oauth2-proxy, Authelia, etc.).
Y el control que más rentabilidad da: VPN. Si tienes Fortigate, por ejemplo, tengo una guía práctica: Configurar VPN SSL en equipos Fortigate.
Una frase que me repito: la autenticación sin control de red es una puerta con cristal. La puedes cerrar, sí, pero sigues exponiéndola a golpes constantes.
Paso 4 — Webhooks: que sean puertas, no agujeros
Los webhooks son el punto donde n8n se asoma al mundo. Y el mundo muerde. Si puedes evitar exponerlos a internet, evítalo.
Objetivo del paso: que un webhook sea un endpoint bien definido, con contrato, y con controles. No un “puerto trasero” para ejecutar cosas.
Checklist de seguridad para webhooks:
- URL pública correcta: configura
N8N_HOST/N8N_PROTOCOLy, si aplica en tu versión,WEBHOOK_URLpara que n8n genere enlaces públicos correctos (y siempre HTTPS). - Paths dedicados: separa UI y webhooks por ruta o incluso por subdominio si puedes (ej:
hooks.tu-dominio.com). - Token o firma: un secreto compartido en header/query, o firma HMAC si el emisor lo soporta.
- Rate limiting: en el proxy, no dentro del workflow.
- Validación estricta: método, content-type, tamaño de body, campos esperados.
Ejemplo: rate limiting y límite de tamaño en Nginx para una ruta de webhooks:
# http { ... }
limit_req_zone $binary_remote_addr zone=hooks_per_ip:10m rate=10r/s;
server {
# ... TLS ...
location /webhook/ {
client_max_body_size 1m;
limit_req zone=hooks_per_ip burst=20 nodelay;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://n8n:5678;
}
}Ejemplo de control simple dentro del workflow (pseudo): validas un header X-Webhook-Token y si no coincide, devuelves 401. No pongo aquí un secreto real. Y sí: mejor en header que en querystring, porque los logs y los referers son traicioneros.
El anti-patrón que más veo: “un webhook genérico” que recibe cualquier cosa y decide qué hacer según el payload. Eso es cómodo… hasta que alguien lo descubre y te empieza a disparar ejecuciones caras, o peor, a encadenar acciones no previstas. Diseña endpoints por intención: uno para “crear ticket”, otro para “registrar evento”, etc.
Paso 5 — Redes: segmentación, mínimos puertos y nada de Postgres a Internet
Si solo aplicas una cosa de redes, que sea esta: segmenta.
- Red pública: el reverse proxy (443).
- Red interna: n8n + Postgres (y lo que toque), sin puertos publicados.
En Docker Compose ya lo insinuamos con internal: true. Eso reduce “accidentes” y obliga a pasar por el proxy, donde puedes meter controles.
Más ideas (según tu madurez):
- Egress control: si puedes, limita a qué destinos puede salir n8n. No necesita hablar con todo Internet, pero sí con tus SaaS.
- Timeouts: en el proxy, para no tener workers colgados.
- DNS interno: usa nombres de servicio (
postgres) en vez de IPs.
Y recuerda lo de X-Forwarded-For y N8N_PROXY_HOPS: si n8n cree que todas las peticiones vienen del proxy, tus logs y tus límites por IP valen lo mismo que un candado pintado.

Paso 6 — Secretos: *_FILE, Docker secrets y una N8N_ENCRYPTION_KEY estable
Si tus secretos están en:
- un
.envque alguien sube a Git sin querer, - un chat,
- o una wiki sin control de acceso…
- un excel en OneDrive
…no tienes gestión de secretos. Tienes una colección de futuros incidentes.
Objetivo del paso: secretos fuera de Git, con permisos mínimos, y con rotación posible.
Opciones realistas:
- Docker secrets (Swarm/Compose con soporte): bien para empezar.
- Secret manager (Vault, AWS Secrets Manager, Azure Key Vault, etc.): mejor si ya lo usas.
Otra cosa que la gente no cuida: N8N_ENCRYPTION_KEY estable. n8n cifra credenciales almacenadas. Si esa clave cambia al recrear el contenedor, puedes quedarte con credenciales imposibles de descifrar. Traducción: todo funciona hasta que reinicias.
Buenas prácticas rápidas:
- Guarda
N8N_ENCRYPTION_KEYcomo secreto gestionado y no la regeneres salvo proceso controlado. - Permisos de fichero: solo lectura para el usuario del proceso.
Paso 7 — Hardening: recortar superficie
Aquí es donde empieza el día 2 de verdad: reducir daño si algo sale mal. Algún dia saldrá mal, tarde o temprano te pasara algo, te lo digo porque sino no tendríamos trabajo, siempre pasa algo, tarde o temprano, mejor que lo tengas planificado.
Filesystem: menos escritura, menos sorpresas
Si puedes, monta el contenedor con filesystem read-only y deja writable solo lo necesario (volúmenes). En Docker esto depende de tu despliegue; no siempre es trivial con n8n porque necesita escribir en su directorio de datos. Pero el objetivo es claro: no des escritura donde no hace falta.
En Compose (conceptual):
services:
n8n:
read_only: true
tmpfs:
- /tmp
volumes:
- n8n_data:/home/node/.n8n:rwSi tu n8n o tus nodos necesitan escribir ficheros (ej. generar PDFs), define rutas explícitas y no “/”.
Nodos peligrosos: no todo vale
n8n es potente porque puede ejecutar cosas. Y eso es exactamente el problema. Nodos que ejecutan comandos, leen ficheros o hacen requests arbitrarias pueden convertirse en la navaja suiza de un atacante si alguien consigue acceso a la UI o a endpoints mal protegidos.
Medidas prácticas:
- Acceso a la UI muy controlado (VPN/allowlist + auth proxy).
- Revisión de workflows son código, siempre lo han sido, trátalos como tal.
- Limita integraciones: credenciales con mínimo privilegio (no uses tokens admin porque sí).
Cookies y cabeceras
Si vas detrás de HTTPS, asegúrate de que la app se comporta como tal (cookies seguras, redirects correctos). Esto depende de versión/config; revisa la doc de n8n y valida con el navegador. Y en el proxy, mete cabeceras sensatas (HSTS si procede, X-Content-Type-Options, etc.). No te va a salvar de todo, pero evita tonterías.
Logging y retención de ejecuciones
n8n guarda ejecuciones. Eso es útil… hasta que el disco se llena. Y entonces deja de ser útil de golpe.
Configura:
- Retención (número de ejecuciones / días) según tu necesidad real. (puedes hacerlo en los logs, sin la enterprise N8n no lo permite)
- Logging con nivel razonable (debug permanente = dolor).
- Alertas por uso de disco y errores repetidos. No se como hay gente que no monitoriza un servidor, se me escapa como algo así puede pasar. Soy bastante FAN de Pandora FMS, pero cualquier sistema similar vale.
Checklist de producción
- [ ] No hay puerto 5678 publicado a Internet. (Si lo hay: apaga y arregla.)
- [ ] Reverse proxy expone solo 443 con TLS válido.
- [ ] Cabeceras
X-Forwarded-Proto/X-Forwarded-Forcorrectas yN8N_PROXY_HOPSrevisado según topología. - [ ] UI de n8n accesible solo por VPN/allowlist + auth en proxy (si no hay SSO).
- [ ] Webhooks con HTTPS, rutas dedicadas, token/firma y rate limiting.
- [ ] Postgres en red interna, sin publicar 5432, con backups probados.
- [ ] Secretos fuera de Git: Docker secrets/secret manager; permisos mínimos.
- [ ]
N8N_ENCRYPTION_KEYestable y respaldada como secreto crítico. - [ ] Retención de ejecuciones configurada + alertas de disco.
- [ ] Versión de n8n fijada y upgrades planificados (con ventana y rollback).
Errores comunes
- Publicar 5678 “solo un rato” → No lo publiques. Proxy con TLS o nada. Si necesitas acceso puntual:
127.0.0.1+ SSH/VPN. - Webhooks sin token/firmas → Añade un secreto por endpoint (header) o firma HMAC si el emisor lo soporta, y valida método/content-type.
- Sin rate limiting → Limita en el proxy. No esperes a que te hagan un DoS “barato” a base de ejecuciones.
- Postgres expuesto → Red interna y sin
ports:. Si necesitas acceso admin, hazlo desde una máquina en la misma red/VPN. - Secretos en .env y Git → Docker secrets o secret manager. Y revisa histórico: borrar el fichero no borra el secreto del repo.
- Cambiar (o perder) la N8N_ENCRYPTION_KEY → Trátala como clave maestra. Sin ella, puedes perder acceso a credenciales cifradas.
- Confiar en la IP real sin ajustar proxy hops → Revisa
X-Forwarded-Fory la configuración de confianza en proxies; si no, tus logs/controles por IP son humo. - Retención infinita de ejecuciones → Ajusta limpieza y monitoriza disco. El “ya lo miraremos” acaba en caída.

Conclusión
Bueno, espero que esta entrega te sirva de algo, la verdad que veo mucha gente pidiendo comandos a GPT y haciendo cosas que me dejan ojiplatico, pero es que encima muchos te lo dicen como en plan, le he preguntado y me ha dado estos comandos. Sabiendo muy bien lo que haces ya es complicado dejar un sistema bien montado, no me quiero ni imaginar que puedes llegar a montar si vas siguiendo el tutorial.
Si te quedas con una idea, que sea esta: n8n no debe exponerse “a pelo” en 5678. Monta el proxy, mete TLS, separa UI y webhooks, y trata secretos + red como si fueran parte del producto. Porque lo son.
Si quieres el día 2 completo (persistencia, backups que restauran, upgrades), enlazo mi guía base: n8n en Docker: rápido de montar, serio de mantener. Despliega en tu empresa.
