Automatizar tareas con n8n es relativamente simple en local. El problema aparece cuando querés exponer flujos reales a Internet: webhooks, integraciones con APIs, formularios, CRMs, sistemas internos o herramientas de marketing.
Ahí ya no alcanza con ejecutar un contenedor y abrir un puerto. Necesitás una URL pública estable, HTTPS, una base de datos persistente, backups y una configuración que no deje expuestos servicios internos.
En esta guía vas a desplegar n8n self-hosted en un Cloud Server con Docker Compose, PostgreSQL, NGINX como reverse proxy y certificado TLS. El objetivo es tener una base sólida para ambientes productivos pequeños, pruebas serias o proyectos internos que necesitan automatización controlada.
¿Qué es n8n?
n8n es una herramienta de automatización de workflows. Permite conectar servicios, APIs, bases de datos y procesos internos mediante flujos visuales.
Podés usarlo para tareas como:
- recibir un webhook y guardar datos en una base;
- sincronizar contactos entre herramientas;
- enviar alertas a Slack, email o Telegram;
- procesar formularios;
- ejecutar integraciones con APIs propias;
- automatizar tareas de soporte, ventas o infraestructura.
La ventaja del modo self-hosted es que controlás dónde corre la instancia, cómo se almacenan las credenciales y qué reglas de red aplicás.
¿Cuándo conviene usar n8n self-hosted?
Conviene usar n8n en tu propio servidor cuando necesitás más control que el que ofrece un servicio gestionado o cuando tus flujos trabajan con datos internos.
Algunos casos claros:
- automatizaciones internas de una empresa;
- integraciones con sistemas propios;
- webhooks que necesitan una URL estable;
- pruebas de automatización antes de escalar;
- flujos que requieren secretos, tokens o credenciales sensibles;
- proyectos donde querés controlar backups, red y políticas de acceso.
No conviene exponer n8n sin HTTPS, sin autenticación o con puertos internos publicados directamente a Internet. n8n puede manejar credenciales sensibles, por lo que la seguridad inicial importa.
Requisitos previos
Para seguir esta guía necesitás:
- un Cloud Server con Ubuntu 22.04 LTS o 24.04 LTS;
- acceso SSH con un usuario con permisos
sudo; - un dominio o subdominio, por ejemplo
automatiza.ejemplo.com; - un registro DNS tipo
Aapuntando a la IP pública del servidor; - puertos
80y443abiertos para tráfico web; - puerto
22abierto solo para administración SSH; - Docker Engine y Docker Compose;
- NGINX;
- Certbot para emitir el certificado TLS.
En esta guía se usará:
80: HTTP, necesario para validación inicial y redirección;443: HTTPS, tráfico público real hacia n8n;5678: puerto interno de n8n, no debe publicarse a Internet;5432: puerto interno de PostgreSQL, no debe publicarse a Internet.
Paso 1: Preparar el servidor
Actualizá los paquetes base:
sudo apt updatesudo apt upgrade -yInstalá herramientas necesarias:
sudo apt install -y ca-certificates curl gnupg ufw nginxConfigurá reglas básicas de firewall. Si administrás el servidor desde una IP fija, lo ideal es limitar SSH a esa IP:
sudo ufw allow 80/tcpsudo ufw allow 443/tcpsudo ufw allow from TU_IP_PUBLICA to any port 22 proto tcpsudo ufw enablesudo ufw status
Si todavía no conocés tu IP fija o estás configurando el servidor por primera vez, podés permitir SSH temporalmente:
sudo ufw allow OpenSSHAdvertencia: antes de activar ufw, verificá que la regla de SSH esté bien configurada. Si cerrás el puerto equivocado, podés perder acceso al servidor.
[Imagen sugerida: preparación inicial del servidor con actualización, NGINX, UFW y SSH]
Paso 2: Instalar Docker Engine

Docker recomienda instalar Docker Engine desde su repositorio oficial en Ubuntu. Primero agregá la clave y el repositorio:
sudo install -m 0755 -d /etc/apt/keyringscurl -fsSL https://download.docker.com/linux/ubuntu/gpg \ | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpgsudo chmod a+r /etc/apt/keyrings/docker.gpgecho \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" \ | sudo tee /etc/apt/sources.list.d/docker.list > /dev/nullLuego instalá Docker y el plugin de Compose:
sudo apt updatesudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginVerificá que Docker funcione:
docker --versiondocker compose versionOpcionalmente, agregá tu usuario al grupo docker para evitar usar sudo en cada comando:
sudo usermod -aG docker $USERDespués cerrá sesión y volvé a entrar por SSH.
Paso 3: Crear la estructura del proyecto
Creá un directorio para n8n:
sudo mkdir -p /opt/n8nsudo chown -R $USER:$USER /opt/n8ncd /opt/n8nGenerá una clave fuerte para cifrar credenciales dentro de n8n:
openssl rand -base64 32Guardá ese valor. Lo vas a usar como N8N_ENCRYPTION_KEY.
Esta clave es importante: si la perdés, podés tener problemas para recuperar credenciales guardadas en n8n después de migraciones o restauraciones.

Paso 4: Crear el archivo .env
Creá un archivo .env:
nano .envPegá una configuración como esta, ajustando dominio, contraseñas y zona horaria:
POSTGRES_DB=n8nPOSTGRES_USER=n8nPOSTGRES_PASSWORD=CAMBIAR_POR_UNA_PASSWORD_LARGAN8N_HOST=automatiza.ejemplo.comN8N_PROTOCOL=httpsWEBHOOK_URL=https://automatiza.ejemplo.com/N8N_PROXY_HOPS=1N8N_ENCRYPTION_KEY=PEGAR_CLAVE_GENERADA_CON_OPENSSLGENERIC_TIMEZONE=America/Argentina/Buenos_AiresTZ=America/Argentina/Buenos_AiresN8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=trueNo uses contraseñas débiles ni valores de ejemplo en producción. El archivo .env contiene secretos y no debería subirse a un repositorio público.
Paso 5: Crear docker-compose.yml
En /opt/n8n, creá el archivo:
nano docker-compose.ymlPegá esta configuración:
services: postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data networks: - n8n_internal n8n: image: n8nio/n8n:latest restart: unless-stopped depends_on: - postgres 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_HOST: ${N8N_HOST} N8N_PROTOCOL: ${N8N_PROTOCOL} WEBHOOK_URL: ${WEBHOOK_URL} N8N_PROXY_HOPS: ${N8N_PROXY_HOPS} N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} GENERIC_TIMEZONE: ${GENERIC_TIMEZONE} TZ: ${TZ} N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: ${N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS} volumes: - n8n_data:/home/node/.n8n ports: - "127.0.0.1:5678:5678" networks: - n8n_internalvolumes: postgres_data: n8n_data:networks: n8n_internal: driver: bridgeLa línea importante es esta:
ports: - "127.0.0.1:5678:5678"Eso hace que n8n escuche solo en localhost. Internet no llega directo al puerto 5678; el acceso público se hace por NGINX en 443.
En producción, conviene fijar una versión concreta de n8n en lugar de usar latest, por ejemplo cuando ya validaste una versión estable para tu flujo de trabajo. latest simplifica la guía, pero puede traer cambios inesperados en actualizaciones.

Paso 6: Iniciar n8n
Desde /opt/n8n, ejecutá:
docker compose up -dVerificá el estado:
docker compose psdocker compose logs -f n8nProbá localmente:
curl http://127.0.0.1:5678Si responde HTML o una redirección, n8n está levantado.

Paso 7: Configurar NGINX como reverse proxy
Creá un archivo de sitio para NGINX:
sudo nano /etc/nginx/sites-available/n8nPegá esta configuración, cambiando el dominio:
server { listen 80; server_name automatiza.ejemplo.com; location / { proxy_pass http://127.0.0.1:5678; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Proto $scheme; }}Activá el sitio:
sudo ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/n8nsudo nginx -tsudo systemctl reload nginxAhora deberías poder abrir:
http://automatiza.ejemplo.comTodavía falta HTTPS. No uses credenciales reales ni conectes servicios externos hasta completar el certificado TLS.
Paso 8: Activar HTTPS con Certbot

Instalá Certbot. En Ubuntu, una opción habitual es usar Snap:
sudo apt install -y snapdsudo snap install --classic certbotsudo ln -s /snap/bin/certbot /usr/bin/certbotEmití el certificado para NGINX:
sudo certbot --nginx -d automatiza.ejemplo.comCertbot puede ajustar la configuración para usar HTTPS y redirigir HTTP a HTTPS.
Verificá renovación automática:
sudo certbot renew --dry-runA partir de este punto, la URL pública debería ser:
https://automatiza.ejemplo.comPaso 9: Configurar correctamente los webhooks
Cuando n8n está detrás de NGINX, hay una diferencia entre el puerto interno y la URL pública.
Internamente n8n escucha en:
http://127.0.0.1:5678Pero los servicios externos tienen que llamar a:
https://automatiza.ejemplo.com/webhook/...Por eso son importantes estas variables:
N8N_HOST=automatiza.ejemplo.comN8N_PROTOCOL=httpsWEBHOOK_URL=https://automatiza.ejemplo.com/N8N_PROXY_HOPS=1Si WEBHOOK_URL queda mal configurada, n8n puede mostrar URLs con localhost, http o puertos internos. Eso rompe integraciones externas aunque el editor visual cargue bien.
Después de cambiar variables, reiniciá los contenedores:
cd /opt/n8ndocker compose up -d
Paso 10: Probar que funciona
Desde el navegador, entrá a:
https://automatiza.ejemplo.comCreá el usuario inicial de n8n y luego armá un flujo simple con un nodo Webhook.
Probá la URL pública con curl:
curl -i https://automatiza.ejemplo.com/webhook/testLa ruta exacta dependerá del webhook que hayas creado. Lo importante es que la URL empiece con https://automatiza.ejemplo.com y no con localhost.
También podés revisar logs:
docker compose logs -f n8nsudo tail -f /var/log/nginx/access.logsudo tail -f /var/log/nginx/error.log
Paso 11: Reforzar seguridad
Antes de usar n8n con datos reales, revisá estos puntos:
- mantené públicos solo
80y443; - no publiques
5678ni5432; - usá una
N8N_ENCRYPTION_KEYfuerte y persistente; - configurá backups de los volúmenes
postgres_datayn8n_data; - usá contraseñas largas y únicas;
- mantené Docker, NGINX y el sistema operativo actualizados;
- revisá permisos del archivo
.env; - evitá guardar tokens innecesarios en flujos de prueba;
- ejecutá auditorías de seguridad de n8n cuando corresponda;
- probá restaurar backups, no solo generarlos.
n8n incluye una función de auditoría de seguridad que puede ayudar a detectar riesgos comunes. Según la documentación oficial, se puede ejecutar con:
docker compose exec n8n n8n auditBackups mínimos recomendados
Para una instalación simple, al menos necesitás respaldar:
- la base PostgreSQL;
- el volumen de datos de n8n;
- el archivo
.env; - el archivo
docker-compose.yml; - la configuración de NGINX.
Un backup básico de PostgreSQL podría verse así:
cd /opt/n8ndocker compose exec postgres pg_dump \ -U "$POSTGRES_USER" \ "$POSTGRES_DB" > backup-n8n.sqlEn algunos shells, las variables del .env no están cargadas automáticamente. Si el comando no toma los valores, podés ejecutar:
source .envy luego repetir el backup.
Advertencia: guardá los backups fuera del servidor o en una ubicación separada. Un backup que vive únicamente en el mismo disco no te protege ante pérdida del servidor, borrado accidental o corrupción del volumen.

Problemas frecuentes
n8n muestra webhooks con localhost
Revisá estas variables:
N8N_HOST=automatiza.ejemplo.comN8N_PROTOCOL=httpsWEBHOOK_URL=https://automatiza.ejemplo.com/N8N_PROXY_HOPS=1Después reiniciá:
docker compose up -dEl navegador muestra error 502 Bad Gateway
NGINX no puede llegar a n8n. Verificá que el contenedor esté activo:
docker compose psdocker compose logs n8nTambién comprobá que n8n responda localmente:
curl http://127.0.0.1:5678Certbot no puede emitir el certificado
Revisá que:
- el DNS apunte a la IP correcta;
- el puerto
80esté abierto; - NGINX esté activo;
- no haya otro servicio usando el puerto
80.
Podés probar NGINX con:
sudo nginx -tsudo systemctl status nginxPostgreSQL no inicia
Revisá logs:
docker compose logs postgresVerificá que no hayas cambiado POSTGRES_USER, POSTGRES_DB o POSTGRES_PASSWORD después de inicializar el volumen. En PostgreSQL, esos valores se usan al crear la base por primera vez; cambiarlos luego no modifica automáticamente la base ya creada.
El servidor queda sin acceso por SSH
Si activaste ufw sin permitir correctamente SSH, podés perder acceso remoto. Antes de activar el firewall, confirmá la regla:
sudo ufw statusSi usás el panel del proveedor cloud y tenés consola de emergencia, podrías corregirlo desde ahí. Aun así, es mejor prevenirlo.
Conclusión
n8n puede ser una pieza muy útil para automatizar procesos, pero cuando lo exponés a Internet conviene tratarlo como cualquier aplicación de producción: dominio, HTTPS, base persistente, reverse proxy, firewall, backups y revisión de seguridad.
Con Docker Compose, PostgreSQL y NGINX podés tener una instalación clara, mantenible y fácil de mover. Si necesitás un servidor para este tipo de despliegues, un Cloud Server de DonWeb te permite elegir Ubuntu, configurar tu stack y escalar recursos cuando tus automatizaciones crezcan.