SincroDev Logo SincroDev

Cómo desplegar n8n de forma segura en un VPS


Levantar n8n con Docker es fácil.

Dejarlo expuesto en un VPS sin convertirlo en un problema de seguridad ya es otra historia.

El patrón peligroso suele ser este:

  • puerto 5678 abierto públicamente
  • credenciales débiles
  • sin SSL
  • sin reverse proxy
  • sin estrategia de backups

Eso puede funcionar para probar. No para operar en serio.

En este tutorial vamos a tomar una instalación básica y llevarla a una forma bastante más razonable para producción.

Qué vas a construir

La arquitectura objetivo será esta:

Internet

Nginx / reverse proxy

HTTPS + dominio

127.0.0.1:5678

n8n en Docker

PostgreSQL

La idea central es simple:

  • n8n no debería quedar expuesto directamente a Internet
  • el acceso público debería pasar por un dominio con SSL
  • la persistencia y autenticación deberían quedar bien definidas

Punto de partida

Si aún no instalaste n8n, primero mira este tutorial:

Ese tutorial te deja una base funcional. Este se enfoca en endurecerla para un entorno real.

Cuándo conviene este enfoque

Tiene sentido cuando:

  • ya usas n8n para procesos importantes
  • quieres exponer webhooks o usarlo con tu equipo
  • necesitas una base más confiable que una demo temporal

No es suficiente cuando:

  • tienes requisitos estrictos de alta disponibilidad
  • necesitas observabilidad centralizada o despliegues complejos multi-entorno

Pero para la mayoría de instalaciones iniciales, esta base ya marca una diferencia fuerte.

Paso 1: No expongas n8n directamente al mundo

Una mala práctica común:

ports:
  - "5678:5678"

Eso deja el servicio disponible públicamente en el puerto del VPS.

Mejor:

ports:
  - "127.0.0.1:5678:5678"

Con eso, n8n solo escucha localmente en el servidor, y el acceso externo pasa por tu reverse proxy.

Paso 2: Usa PostgreSQL, no SQLite, si el entorno importa

Para pruebas pequeñas, SQLite puede alcanzar.

Pero si la instancia empieza a ser importante, conviene usar PostgreSQL:

  • mejor robustez
  • mejor consistencia
  • mejor recuperación
  • mejor base para crecer

Una configuración mínima sería:

services:
  postgres:
    image: postgres:16-alpine
    restart: always
    environment:
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=tu_password_seguro
      - POSTGRES_DB=n8n
    volumes:
      - db_storage:/var/lib/postgresql/data

  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: always
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=tu_password_seguro

Paso 3: Activa autenticación básica

Si vas a exponer el panel de n8n, no puede quedar abierto.

Como mínimo:

environment:
  - N8N_BASIC_AUTH_ACTIVE=true
  - N8N_BASIC_AUTH_USER=admin
  - N8N_BASIC_AUTH_PASSWORD=una_clave_larga_y_unica

No es el mecanismo más sofisticado del mundo, pero es mucho mejor que nada.

Claves importantes:

  • no uses admin/admin
  • no reutilices contraseñas
  • guarda secretos fuera del repo si el entorno ya es serio

Paso 4: Configura dominio y URL pública correctamente

Si n8n va a generar links, callbacks o webhooks, necesita saber cuál es su URL pública real.

Variables típicas:

environment:
  - N8N_HOST=n8n.tudominio.com
  - N8N_PROTOCOL=https
  - WEBHOOK_URL=https://n8n.tudominio.com/

Sin esto, algunos flujos terminan generando rutas incorrectas o inconsistentes.

Paso 5: Coloca un reverse proxy delante

La configuración exacta depende de tu host, pero el patrón debería ser:

https://n8n.tudominio.com -> 127.0.0.1:5678

Eso te permite:

  • manejar SSL fuera del contenedor
  • centralizar certificados
  • controlar acceso
  • mantener el servicio interno no expuesto

Paso 6: Usa HTTPS desde el primer día

Si vas a:

  • iniciar sesión
  • exponer webhooks
  • conectar credenciales
  • trabajar con datos reales

entonces necesitas HTTPS.

No lo dejes “para después”.

Como mínimo, asegura el dominio con certificados válidos y fuerza redirección a HTTPS en el reverse proxy.

Paso 7: Añade healthchecks y restart policy

No basta con que el contenedor exista. Conviene saber si responde.

Ejemplo simple:

healthcheck:
  test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:5678"]
  interval: 30s
  timeout: 5s
  retries: 3

Y usa:

restart: always

Esto no reemplaza monitoreo real, pero mejora bastante el comportamiento operativo inicial.

Paso 8: Haz backups desde temprano

Si n8n importa para tu operación, hay dos cosas que debes poder recuperar:

  • la base de datos
  • la configuración persistida

Como mínimo, asegúrate de respaldar:

  • volumen de PostgreSQL
  • volumen o path de configuración de n8n

No esperes al primer error para descubrir que no puedes restaurar nada.

Paso 9: Piensa la seguridad de los webhooks

Abrir n8n al mundo también significa abrir endpoints de webhook.

Eso requiere criterio:

  • valida origen cuando aplique
  • usa firmas o tokens si la fuente lo soporta
  • limita lo que aceptan los workflows
  • evita dejar endpoints públicos sin control

No todos los riesgos están en el panel de administración. Muchos viven en los webhooks mal diseñados.

Paso 10: Separa credenciales del código

Una instalación “que funciona” suele terminar con secretos hardcodeados en el compose.

Eso es tolerable para una prueba local. No para producción.

Evolución mínima razonable:

  • variables de entorno
  • archivos .env
  • manejo más serio de secretos si el entorno escala

La regla es simple: si alguien sube ese archivo al repositorio, no deberías comprometer toda la instancia.

Ejemplo de compose más razonable

version: '3.8'

volumes:
  db_storage:
  n8n_storage:

services:
  postgres:
    image: postgres:16-alpine
    restart: always
    environment:
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=n8n
    volumes:
      - db_storage:/var/lib/postgresql/data

  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: always
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
      - N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
      - N8N_HOST=n8n.tudominio.com
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.tudominio.com/
    volumes:
      - n8n_storage:/home/node/.n8n
    depends_on:
      - postgres

Errores comunes

1) Dejar 5678 abierto públicamente

Es de los errores más típicos y más innecesarios.

2) Usar credenciales débiles

Si el panel queda expuesto con autenticación trivial, no tienes una instalación segura.

3) No configurar WEBHOOK_URL

Muchos problemas raros de callbacks nacen ahí.

4) No tener backups

Hasta que pierdes workflows o credenciales y descubres que el contenedor “arrancaba bien” pero el negocio no.

5) No controlar endpoints públicos

El panel no es el único vector. Los webhooks también importan.

Qué añadir después

Una vez cubierta esta base, los siguientes pasos razonables serían:

  • monitoreo
  • backups automatizados
  • rotación de logs
  • actualizaciones controladas
  • entornos separados
  • proxy con reglas más finas

No hace falta hacer todo a la vez. Pero sí conviene dejar una instalación que no dependa de suerte.

Resumen

Para desplegar n8n de forma más segura en un VPS necesitas:

  1. puertos internos, no públicos
  2. PostgreSQL en lugar de una base frágil
  3. autenticación básica fuerte
  4. dominio y HTTPS
  5. reverse proxy
  6. backups
  7. control sobre webhooks y secretos

No es blindaje absoluto. Pero sí te saca del terreno de “instancia expuesta y rezando”.

Si quieres dejar una instalación de n8n bien montada, conectarla con tus procesos o convertir workflows dispersos en automatizaciones mantenibles, puedes escribirme desde Sobre Mí.