Instalar n8n con Docker Compose en un VPS
Configura n8n 2.x con Docker Compose y PostgreSQL en un VPS. Listo para producción desde el inicio con versiones fijadas, respaldo de la clave de cifrado, health checks y límites de recursos.
Esta guía instala n8n 2.12.3 con Docker Compose y PostgreSQL en un VPS. Tendrás una instancia de n8n funcionando en unos 15 minutos. La configuración usa valores de producción desde el inicio: versiones de imagen fijadas, credenciales cifradas, puerto vinculado solo a localhost, health checks en ambos contenedores y límites de recursos Docker. No se cubre reverse proxy ni SSL aquí. Eso está en .
¿Qué necesito antes de instalar n8n?
Necesitas un VPS con Debian 12 o Ubuntu 24.04 y al menos 4 GB de RAM. Docker y Docker Compose (el plugin docker compose, no el antiguo binario docker-compose) deben estar instalados. Si aún no tienes Docker, sigue primero Docker Compose para despliegues multi-servicio en VPS.
Verifica que Docker Compose esté disponible:
docker compose version
Salida esperada:
Docker Compose version v2.x.x
Si este comando falla con docker: 'compose' is not a docker command, tienes el binario standalone antiguo. Instala el plugin Docker Compose desde el repositorio oficial de Docker.
También necesitas un usuario no-root con acceso sudo. Todos los comandos de esta guía se ejecutan con ese usuario, no como root.
¿Por qué usar PostgreSQL en lugar de SQLite para n8n?
PostgreSQL maneja conexiones concurrentes, soporta WAL para recuperación ante fallos y funciona con pg_dump para respaldos en caliente mientras n8n está corriendo. SQLite bloquea todo el archivo de base de datos en cada escritura. Con ejecuciones concurrentes de webhooks, esto provoca timeouts y corrupción de datos. No se puede respaldar una base de datos SQLite de forma segura mientras n8n está corriendo sin arriesgar una copia corrupta. Para cualquier uso más allá de pruebas locales, PostgreSQL es la opción correcta.
| Característica | SQLite | PostgreSQL |
|---|---|---|
| Escrituras concurrentes | Bloqueo de escritor único | MVCC completo |
| Respaldos en caliente | Inseguro en ejecución | pg_dump en cualquier momento |
| Recuperación ante fallos | Replay manual del journal | Replay automático del WAL |
| Escalabilidad | Proceso único | Connection pooling |
| Variable de entorno n8n | DB_TYPE=sqlite |
DB_TYPE=postgresdb |
¿Cómo instalo n8n con Docker Compose en un VPS?
Crea un directorio de proyecto, escribe un archivo .env con tus secretos, escribe el docker-compose.yml, inicia el stack y verifica que todo esté saludable. Cada paso incluye una verificación.
Crear el directorio del proyecto
mkdir -p ~/n8n && cd ~/n8n
Crear el archivo .env
Todos los secretos van en un archivo .env. Nunca escribas contraseñas o claves directamente en docker-compose.yml.
Genera una contraseña de base de datos fuerte y la clave de cifrado de n8n:
echo "POSTGRES_PASSWORD=$(openssl rand -base64 32)" >> .env
echo "N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)" >> .env
Ahora agrega las variables restantes:
cat >> .env << 'EOF'
# PostgreSQL
POSTGRES_USER=n8n
POSTGRES_DB=n8n
# n8n
N8N_VERSION=2.12.3
N8N_HOST=localhost
N8N_PORT=5678
N8N_PROTOCOL=http
N8N_DIAGNOSTICS_ENABLED=false
GENERIC_TIMEZONE=UTC
EOF
Restringe los permisos del archivo. Solo tu usuario debe poder leerlo:
chmod 600 .env
Verificación:
ls -la .env
Deberías ver -rw-------. Ningún otro usuario en el servidor puede leer tu contraseña de base de datos ni tu clave de cifrado.
¿Cómo genero y respaldo la clave de cifrado de n8n?
El N8N_ENCRYPTION_KEY se generó arriba con openssl rand -hex 32. Esto produce una clave aleatoria de 32 bytes (64 caracteres hexadecimales). n8n usa esta clave para cifrar todas las credenciales que almacenas: claves API, tokens OAuth, contraseñas de bases de datos en workflows. Si pierdes esta clave, todas las credenciales almacenadas quedan permanentemente ilegibles. No existe mecanismo de recuperación.
Respalda la clave de cifrado ahora. Cópiala a un gestor de contraseñas o una bóveda offline:
grep N8N_ENCRYPTION_KEY .env
Guarda el resultado en un lugar seguro fuera de este servidor. Hazlo antes de agregar tu primera credencial en n8n.
Referencia de variables de entorno
| Variable | Propósito | Valor de ejemplo |
|---|---|---|
POSTGRES_USER |
Nombre del superusuario PostgreSQL | n8n |
POSTGRES_PASSWORD |
Contraseña del superusuario PostgreSQL | (generada, 32+ caracteres) |
POSTGRES_DB |
Nombre de la base de datos | n8n |
N8N_VERSION |
Tag de imagen n8n fijado | 2.12.3 |
N8N_ENCRYPTION_KEY |
Cifra las credenciales almacenadas | (generada, 64 caracteres hex) |
N8N_HOST |
Host para la interfaz de n8n | localhost |
N8N_PORT |
Puerto para la interfaz de n8n | 5678 |
N8N_PROTOCOL |
HTTP o HTTPS | http |
N8N_DIAGNOSTICS_ENABLED |
Enviar telemetría a n8n | false |
GENERIC_TIMEZONE |
Zona horaria para triggers cron | UTC |
Escribir el docker-compose.yml
cat > docker-compose.yml << 'COMPOSE'
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
security_opt:
- no-new-privileges:true
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
n8n:
image: docker.n8n.io/n8nio/n8n:${N8N_VERSION}
restart: unless-stopped
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_HOST}
N8N_PORT: ${N8N_PORT}
N8N_PROTOCOL: ${N8N_PROTOCOL}
N8N_DIAGNOSTICS_ENABLED: ${N8N_DIAGNOSTICS_ENABLED}
GENERIC_TIMEZONE: ${GENERIC_TIMEZONE}
ports:
- "127.0.0.1:5678:5678"
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:5678/healthz || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
deploy:
resources:
limits:
memory: 2G
cpus: "2.0"
security_opt:
- no-new-privileges:true
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
volumes:
postgres_data:
n8n_data:
COMPOSE
Algunos puntos a notar en este archivo:
Sin clave version:. Docker Compose V2 la ignora. Todos los tutoriales de la competencia aún incluyen version: '3.7' o version: '3.8'. La especificación Compose depreció este campo. Incluirlo produce una advertencia en las versiones actuales de Docker.
Puerto vinculado a 127.0.0.1. La línea "127.0.0.1:5678:5678" restringe n8n a localhost. Esto es un requisito de seguridad, no una preferencia. Los mapeos de puertos de Docker evitan iptables y las reglas UFW por completo. Si escribes 5678:5678 sin el prefijo 127.0.0.1, n8n es accesible desde internet aunque tu firewall bloquee el puerto 5678. Un reverse proxy en la misma máquina reenviará el tráfico a localhost:5678 cuando lo configures.
security_opt: no-new-privileges:true evita que los procesos dentro del contenedor obtengan privilegios adicionales mediante binarios setuid o setgid. Es una medida de defensa en profundidad contra ataques de escape de contenedor.
Rotación de logs. El bloque logging limita el log JSON de cada contenedor a 3 archivos de 10 MB cada uno (30 MB máximo por servicio). Sin esto, los logs de Docker crecen hasta llenar tu disco. En un VPS con almacenamiento limitado, esto importa.
Health checks en ambos servicios. PostgreSQL usa pg_isready. n8n usa su endpoint /healthz. La condición depends_on asegura que n8n solo inicie después de que PostgreSQL pase su health check.
Límites de recursos. PostgreSQL obtiene 512 MB de RAM y 1 CPU. n8n obtiene 2 GB de RAM y 2 CPUs. Estos valores funcionan bien en un VPS de 4-8 GB. Ajústalos según el tamaño de tu servidor y la complejidad de tus workflows.
Volúmenes nombrados. Tanto postgres_data como n8n_data son volúmenes nombrados gestionados por Docker. Docker maneja la propiedad y los permisos dentro del volumen automáticamente. No necesitas crear directorios en el host ni corregir permisos manualmente.
restart: unless-stopped en lugar de restart: always. Ambos reinician tras un fallo, pero unless-stopped respeta los comandos manuales docker compose stop. Con restart: always, un contenedor detenido manualmente reinicia si el daemon Docker reinicia (por ejemplo, tras una actualización del sistema).
Iniciar el stack
cd ~/n8n
docker compose up -d
Observa los logs durante el primer inicio:
docker compose logs -f
Espera hasta ver que n8n muestre una línea con n8n ready on. Presiona Ctrl+C para salir del visor de logs.
Verificar la instalación
Comprueba que ambos contenedores estén corriendo y saludables:
docker compose ps
Salida esperada:
NAME IMAGE ... STATUS PORTS
n8n-n8n-1 docker.n8n.io/n8nio/n8n:2.12.3 ... Up X minutes (healthy) 127.0.0.1:5678->5678/tcp
n8n-postgres-1 postgres:16-alpine ... Up X minutes (healthy)
Fíjate bien: ambos contenedores muestran (healthy) en la columna STATUS. Esto confirma que los health checks están pasando. Si ves (health: starting), espera 30 segundos y verifica de nuevo.
Prueba la API de n8n desde el servidor:
curl -s http://localhost:5678/healthz
Salida esperada:
{"status":"ok"}
Verifica que el puerto solo escuche en localhost, no en todas las interfaces:
ss -tlnp | grep 5678
Deberías ver 127.0.0.1:5678 en la salida. Si ves 0.0.0.0:5678, tu binding de puerto es incorrecto. Detén el stack, corrige la línea ports en docker-compose.yml y reinicia.
¿Cómo creo la cuenta de propietario de n8n?
Abre un túnel SSH desde tu máquina local para acceder a n8n desde tu navegador:
ssh -L 5678:127.0.0.1:5678 your-user@your-server-ip
Ahora abre http://localhost:5678 en tu navegador. n8n muestra una pantalla de configuración en el primer acceso. Crea tu cuenta de propietario con una contraseña fuerte. Esta cuenta tiene acceso de administrador completo a n8n.
Después de crear la cuenta, cierra el túnel SSH. No dejes el puerto 5678 tunelizado más tiempo del necesario. Configura un reverse proxy con SSL para el acceso regular. Ver .
¿Cómo verifico que n8n está funcionando correctamente?
Después de crear tu cuenta y cerrar el túnel SSH, ejecuta una serie final de verificaciones desde el servidor.
Comprueba la salud de los contenedores:
docker compose ps --format "table {{.Name}}\t{{.Status}}"
Ambos servicios deben mostrar (healthy).
Revisa los logs de n8n en busca de errores:
docker compose logs n8n --tail 20
Busca líneas ERROR. Un inicio limpio muestra mensajes de migración de base de datos seguidos de n8n ready on.
Revisa los logs de PostgreSQL:
docker compose logs postgres --tail 10
Deberías ver database system is ready to accept connections.
Revisa el uso de disco de tus volúmenes Docker:
docker system df -v | grep -E "n8n|postgres"
Esto te dice cuánto espacio están usando n8n y PostgreSQL. Revísalo periódicamente en instancias VPS con almacenamiento limitado.
¿Algo salió mal?
El contenedor se detiene inmediatamente. Revisa los logs con docker compose logs n8n. Causas comunes: archivo .env faltante, contraseña de PostgreSQL incorrecta o la clave de cifrado contiene caracteres especiales que rompen la expansión de shell. Regenera tu .env si es necesario.
Errores de permisos. El contenedor de n8n corre como UID 1000. Si cambias de volúmenes nombrados a bind mounts, asegúrate de que el directorio del host pertenezca al UID 1000: sudo chown -R 1000:1000 ./n8n_data.
Health check fallando. n8n necesita 20-30 segundos para iniciar en el primer arranque mientras ejecuta las migraciones de base de datos. Si los health checks fallan, revisa docker compose logs n8n para errores de migración. La configuración start_period: 30s le da tiempo a n8n antes de que comiencen los health checks.
No puedo conectarme a n8n. El puerto está vinculado a localhost. No puedes acceder desde otra máquina sin un túnel SSH o reverse proxy. Esto es intencional.
Olvidé la clave de cifrado. Si perdiste N8N_ENCRYPTION_KEY y n8n tiene credenciales almacenadas, esas credenciales se perdieron. No hay recuperación posible. Por eso existe el paso de respaldo.
¿Qué debo hacer después de instalar n8n?
Esta instalación te da una instancia de n8n funcionando, accesible solo desde el servidor mismo. Para uso en producción, necesitas tres cosas más:
-
Reverse proxy con SSL. Configura Nginx o Caddy frente a n8n con un certificado TLS. Esto te da acceso HTTPS con un nombre de dominio. Ver .
-
Respaldos. Programa respaldos automatizados de la base de datos PostgreSQL y la clave de cifrado de n8n. Ver .
-
Actualizaciones. Para actualizar n8n, cambia
N8N_VERSIONen.enva la nueva versión y luego ejecutadocker compose up -d. Docker descarga la nueva imagen y recrea el contenedor. Lee siempre las notas de versión de n8n antes de actualizar.
Para la guía principal sobre opciones de automatización de workflows en un VPS, ver .
Para los fundamentos de Docker Compose y gestión de múltiples servicios, ver Docker Compose para despliegues multi-servicio en VPS.
Copyright 2026 Virtua.Cloud. Todos los derechos reservados. Este contenido es una obra original del equipo de Virtua.Cloud. La reproducción, republicación o redistribución sin permiso escrito está prohibida.
¿Listo para probarlo?
Despliega tu propio servidor en segundos. Linux, Windows o FreeBSD.
Ver planes VPS