Si alguna vez te preguntaste cómo hacer una instalación limpia, segura y lista para producción ligera/mediana de n8n self-hosted en un servidor Debian, usando Docker Compose, PostgreSQL (mucho más recomendable que SQLite si planeas crecer), Nginx como reverse proxy y certificados HTTPS gratuitos de Let’s Encrypt, esta guía es para ti.
El enfoque es darte un camino sólido con buenas prácticas: persistencia de datos, webhooks públicos que funcionen, soporte para task runners (para ejecutar código de forma más eficiente y segura) y HTTPS obligatorio.
Requisitos previos
Antes de empezar, asegúrate de tener lo siguiente:
- Un VPS con Debian 12 (Bookworm) o superior: versiones estables y bien soportadas tanto por Docker como por n8n.
- Acceso root o con privilegios sudo: lo vas a necesitar para instalar paquetes, configurar el firewall y manejar servicios.
- Un dominio apuntando al IP del servidor (registro A): imprescindible para que los webhooks funcionen desde fuera y para tener HTTPS decente.
- Recomendado: al menos 4 GB de RAM y 2 vCPU: n8n es ligero al principio, pero con varios workflows activos y task runners la memoria sube rápido.
1. Preparación del servidor
Vamos a dejar el sistema actualizado, un poco más seguro y listo para trabajar en producción básica. Primero, actualiza todo:
sudo apt update && sudo apt upgrade -y
Esto refresca la lista de paquetes y aplica los parches de seguridad pendientes. Hazlo siempre antes de instalar cosas nuevas.Instalamos algunas utilidades básicas que vamos a necesitar:
sudo apt install -y curl git ufw apt-transport-https ca-certificates gnupg lsb-release
Con esto tienes herramientas para descargar cosas de forma segura, configurar el firewall y añadir repositorios externos.
Tip de seguridad importante: no trabajes todo el rato como root. Crea un usuario normal:
adduser n8nuser
usermod -aG sudo n8nuser
Elige una contraseña decente. Luego cambia a ese usuario:
De ahora en adelante trabajamos como n8nuser (con sudo cuando haga falta). Es mucho más seguro.
Configuramos un firewall básico con ufw (solo abrimos lo estrictamente necesario):
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable
Con esto bloqueas todo lo demás. SSH, HTTP y HTTPS son los únicos puertos abiertos hacia Internet
2. Instalar Docker y Docker Compose
Docker nos va a permitir correr n8n y PostgreSQL de forma aislada, reproducible y fácil de actualizar.Añadimos el repositorio oficial de Docker (es la forma recomendada para tener versiones actualizadas):
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Actualizamos e instalamos:
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
Nota: desde hace tiempo Docker Compose ya viene como plugin (docker compose, sin guión). Olvídate del antiguo docker-compose en Python.
Verificamos que todo quedó bien:
docker –version
docker compose version
Si ves las versiones, perfecto, seguimos.
3. Crear la estructura y el archivo docker-compose.yml
Lo organizamos todo en un directorio dedicado para que sea fácil hacer backups, moverlo o clonar la configuración:
mkdir -p ~/n8n && cd ~/n8n
mkdir data postgres-data
La carpeta data va a guardar las configuraciones, credenciales y archivos que genere n8n.
docker-compose.yml
Aquí definimos los servicios: PostgreSQL (base de datos persistente recomendada para producción), n8n y los task runners (para ejecutar workflows de forma más eficiente y desacoplada).
Puntos clave que vale la pena entender:
- Usamos networks internas: los contenedores hablan entre sí sin exponer puertos al exterior.
- Volumes: para que los datos sobrevivan reinicios, actualizaciones o incluso recrear contenedores.
- Variables de entorno N8N_*: controlan la URL pública, el cifrado de credenciales, el modo de ejecución, etc.
Muy importante: cambia todas las contraseñas y claves por valores largos, únicos y secretos. Puedes generar una buena clave de cifrado con:
openssl rand -base64 32
Muy importante: guárdala bien, la vas a necesitar si alguna vez tienes que migrar o recuperar datos.
Crea un archivo .env (es muy importante para no dejar contraseñas en el yml):
# .env - ¡NO lo subas a git ni lo compartas!
DOMAIN=app.tudominio.com # Cambia esto
POSTGRES_PASSWORD=tu_contraseña_larga_y_segura
N8N_ENCRYPTION_KEY=$(openssl rand -base64 32) # Genera una clave fuerte
N8N_RUNNERS_AUTH_TOKEN=$(openssl rand -base64 48) # Token secreto para runners
Ahora el docker-compose.yml (copia y pega este archivo en ~/n8n/docker-compose.yml):
services:
postgres:
image: postgres:16
restart: always
environment:
POSTGRES_USER: n8n_user
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: n8n_db
volumes:
- ./postgres-data:/var/lib/postgresql/data
networks:
- n8n_net
n8n:
image: n8nio/n8n:latest
restart: always
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n_db
- DB_POSTGRESDB_USER=n8n_user
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- N8N_PROTOCOL=https
- N8N_HOST=${DOMAIN}
- WEBHOOK_URL=https://${DOMAIN}/
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
# Task Runners – recomendado para producción (ejecuta código de Code nodes de forma aislada y eficiente)
- N8N_RUNNERS_ENABLED=true
- N8N_RUNNERS_MODE=external
- N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0
- N8N_RUNNERS_AUTH_TOKEN=${N8N_RUNNERS_AUTH_TOKEN}
# Descomenta si usas Code nodes en Python nativo:
# - N8N_NATIVE_PYTHON_RUNNER=true
volumes:
- ./data:/home/node/.n8n
depends_on:
- postgres
networks:
- n8n_net
task-runners:
image: n8nio/runners:latest
restart: always
environment:
- N8N_RUNNERS_TASK_BROKER_URI=http://n8n:5679
- N8N_RUNNERS_AUTH_TOKEN=${N8N_RUNNERS_AUTH_TOKEN}
- N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT=15
# Descomenta si necesitas módulos Python stdlib completos (con precaución):
# - N8N_RUNNERS_STDLIB_ALLOW=*
depends_on:
- n8n
networks:
- n8n_net
networks:
n8n_net:
driver: bridge
¿Por qué estas variables?
- WEBHOOK_URL: le dice a n8n cuál es la URL pública real (imprescindible detrás de proxy/Nginx para que los webhooks funcionen desde fuera y aparezcan bien en el editor).
- N8N_ENCRYPTION_KEY: cifra las credenciales guardadas (sin esto, datos sensibles quedan en claro).
- Task runners en modo external: separan la ejecución de código (Code nodes, AI agents, etc.) del proceso principal de n8n. Más seguro y escalable. El token de auth evita que cualquiera conecte runners falsos.
4. Arrancar n8n por primera vez
Ya está todo listo. Subimos los servicios:
docker compose up -d
Y miramos los logs en tiempo real para ver que no haya errores graves:
docker compose logs -f n8n
¿Qué debes ver para saber que va bien?
- El servidor web de n8n arrancó correctamente.
- El broker de tareas está escuchando.
- Los task runners se conectaron sin problemas.
Si algo falla (credenciales de Postgres, puerto ocupado, etc.), los logs te lo dirán clarito.
5. Configurar Nginx como reverse proxy
No exponemos directamente el puerto 5678 de n8n a Internet. Usamos Nginx para que haga de intermediario, maneje HTTPS y preserve las IP reales de los clientes.
Instalamos Nginx:
sudo apt install -y nginx
Crea el archivo de configuración en /etc/nginx/sites-available/n8n.conf (o el nombre que prefieras):
server {
listen 80;
server_name ${DOMAIN}; # Sustituye por tu dominio real o usa $DOMAIN si lo defines como variable
# Redirige todo HTTP → HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name ${DOMAIN};
# Proxy a n8n (puerto interno)
location / {
proxy_pass http://127.0.0.1:5678;
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-Proto $scheme;
# Soporte WebSocket (imprescindible para el editor en tiempo real)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Activa y prueba:
sudo ln -s /etc/nginx/sites-available/n8n.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Con esto n8n ya debería ser accesible por tu dominio (todavía en HTTP).
6. Poner HTTPS con Let’s Encrypt
Ahora sí, ciframos todo con un certificado gratuito y confiable.
Instalamos Certbot:
sudo apt install -y certbot python3-certbot-nginx
Y obtenemos el certificado (sustituye por tu dominio real):
sudo certbot --nginx -d app.tudominio.com
Certbot va a:
- Verificar que el dominio apunta a tu servidor.
- Modificar automáticamente la config de Nginx para usar HTTPS.
- Configurar la renovación automática (via systemd timer).
¡Listo! Desde ahora todo va por HTTPS.
7. Pruebas finales y recomendaciones prácticas
Ya casi estamos pa la candela. Algunas cosas que deberías verificar:
- Accede por primera vez a https://app.tudominio.com –> el primer usuario que crees será el owner (administrador con todos los privilegios).
- Prueba crear un webhook simple y ejecútalo desde fuera (Postman, curl, etc.) para confirmar que la URL pública funciona.
- Revisa que no quede el puerto 5678 abierto hacia Internet (sudo ss -tuln | grep 5678 debería mostrar solo 127.0.0.1).
Buenas prácticas finales (no te las saltes):
- Nunca expongas directamente el puerto 5678.
- Echa un ojo a los logs después de cada cambio o actualización (docker compose logs -f).
- Haz backups periódicos: el volumen data, la base PostgreSQL (pg_dump), y el .env con las claves.
- Actualiza los contenedores cada 1–2 meses de forma controlada: docker compose pull && docker compose up -d.
- Si ves que crece mucho la carga, considera activar queue mode completo con Redis (pero eso ya es otro nivel).
Resultado: tienes una instancia de n8n segura, escalable a nivel básico/mediano y preparada para producción ligera.
Si quieres profundizar más, la documentación oficial es excelente: https://docs.n8n.io/hosting/
Listo. Ya tienes una instancia de n8n bien montada, segura y lista para usarse en serio.
Ojalá esta guía te ahorre tiempo y errores comunes. Si la usas o la adaptas, me encantaría saber cómo te fue. Seguimos administrando.