Desplegando n8n en Docker con Nginx y SSL — arrflows
DevOps 28 de abril, 2026 · 10 min de lectura

Desplegando n8n en Docker con Nginx y SSL en producción

Cómo montar un servidor de automatización privado, seguro y escalable en cualquier VPS por menos de 5€/mes. Sin nubes de pago, sin vendor lock-in, con backups y configuración real de producción.

AR
Antonio Ruiz
Lead Automation Engineer

Por qué self-hosted y no n8n Cloud

n8n ofrece una versión Cloud gestionada con planes desde 20€/mes con ejecuciones limitadas. Para casos de uso simples está bien — pero en cuanto el volumen crece o los datos no pueden salir del perímetro, self-hosted es la única opción viable.

n8n Cloud

20€

/ mes — plan Starter

2.500 ejecuciones/mes

5 workflows activos

Datos en cloud de terceros

Sin acceso al backend

Self-hosted (VPS)

4€

/ mes — Hetzner CX22

Ejecuciones ilimitadas

Workflows ilimitados

Datos en tu infraestructura

Control total + plugins

Para entornos críticos donde los datos no pueden salir del perímetro — banca, salud, telecom, administración pública — self-hosted no es opción: es obligatorio. Pero incluso para casos no regulados, la diferencia de coste y flexibilidad hace que la decisión sea evidente cuando el volumen escala.

Arquitectura objetivo

El stack que vamos a desplegar:

  • n8n en contenedor Docker, conectado a PostgreSQL externo (no SQLite — explico por qué más abajo)
  • PostgreSQL 15 en otro contenedor, con volumen persistente
  • Nginx en el host como reverse proxy, terminando SSL y enrutando hacia n8n
  • Let's Encrypt con renovación automática
  • Backups automáticos de la base de datos a almacenamiento externo

Paso 1: Provisión del VPS

Recomendaciones por proveedor (mayo 2026):

  • Hetzner CX22: 2 vCPU, 4GB RAM, 40GB SSD — 4,15€/mes. La mejor relación precio/calidad para este caso de uso.
  • DigitalOcean Basic: 1 vCPU, 1GB RAM — 6$/mes. Suficiente para empezar pero se queda corto rápido si el workflow es pesado.
  • OVH VPS Starter: opción europea con buena conectividad si tienes integraciones con datacenters franceses o españoles.

Sistema operativo recomendado: Debian 12 o Ubuntu 24.04 LTS. Ambos están bien soportados, con paquetes recientes y ciclos de vida largos.

Paso 2: Hardening básico antes de nada

Antes de instalar nada, asegura el servidor. Estos pasos son la diferencia entre tener un VPS y tener un VPS comprometido:

bash
# Crear usuario no-root
adduser deploy
usermod -aG sudo deploy
usermod -aG docker deploy

# Configurar SSH solo con clave (deshabilitar password)
mkdir -p /home/deploy/.ssh
cp ~/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh

# /etc/ssh/sshd_config
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes

systemctl restart sshd

# Firewall: solo SSH y HTTP/HTTPS
ufw default deny incoming
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

# fail2ban contra fuerza bruta SSH
apt install fail2ban -y
systemctl enable --now fail2ban
Verifica que puedes conectar como deploy antes de cerrar la sesión root. Si algo va mal, vas a necesitar la consola del proveedor para arreglarlo.

Paso 3: Instalar Docker

bash
# Script oficial de Docker
curl -fsSL https://get.docker.com | sudo sh

# Verificar instalación
docker --version
docker compose version

# Test rápido
docker run hello-world

Paso 4: docker-compose.yml de producción

Crea un directorio ~/n8n y dentro coloca este archivo. Es la configuración real que usamos en producción, sin secretos hardcodeados:

yaml
# ~/n8n/docker-compose.yml
services:
  postgres:
    image: postgres:15-alpine
    restart: always
    environment:
      POSTGRES_DB: n8n
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - pg_data:/var/lib/postgresql/data
    networks:
      - internal
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n"]
      interval: 10s
      timeout: 5s
      retries: 5

  n8n:
    image: n8nio/n8n:latest
    restart: always
    ports:
      - "127.0.0.1:5678:5678"  # solo localhost — Nginx hace de proxy
    environment:
      - N8N_HOST=${N8N_HOST}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://${N8N_HOST}/
      - N8N_ENCRYPTION_KEY=${ENCRYPTION_KEY}
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=${DB_PASSWORD}
      - N8N_LOG_LEVEL=info
      - EXECUTIONS_DATA_PRUNE=true
      - EXECUTIONS_DATA_MAX_AGE=336  # 14 días
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - internal

volumes:
  pg_data:
  n8n_data:

networks:
  internal:
    driver: bridge

Y el archivo .env en el mismo directorio:

bash
# ~/n8n/.env
N8N_HOST=n8n.tudominio.com
DB_PASSWORD=$(openssl rand -base64 32)
ENCRYPTION_KEY=$(openssl rand -base64 32)
Nunca uses SQLite en producción para n8n. Con workflows complejos y ejecuciones concurrentes, SQLite genera bloqueos que paralizan todo. PostgreSQL es la opción correcta y la diferencia de complejidad es mínima.
🔑La ENCRYPTION_KEY es crítica: cifra las credenciales que guardas en n8n. Si la pierdes, pierdes acceso a las credenciales. Si la cambias, se invalidan todas las credenciales existentes. Hazle backup en un gestor seguro (Bitwarden, 1Password) antes de arrancar.

Paso 5: Nginx como reverse proxy

Instala Nginx en el host (no en Docker — es más fácil gestionar SSL así):

bash
sudo apt install nginx certbot python3-certbot-nginx -y

Configuración del sitio en /etc/nginx/sites-available/n8n:

nginx
# /etc/nginx/sites-available/n8n
server {
    listen 80;
    server_name n8n.tudominio.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name n8n.tudominio.com;

    # Certificados — los crea certbot en el siguiente paso
    ssl_certificate /etc/letsencrypt/live/n8n.tudominio.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.tudominio.com/privkey.pem;

    # Hardening SSL
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    add_header Strict-Transport-Security "max-age=31536000";

    # Tamaño máximo de petición — útil para workflows que reciben archivos
    client_max_body_size 50M;

    location / {
        proxy_pass http://localhost:5678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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;
        proxy_cache_bypass $http_upgrade;

        # Importante: timeouts largos para webhooks y SSE
        proxy_read_timeout 86400;
        proxy_send_timeout 86400;
    }
}

Activa el sitio y obtén el certificado SSL:

bash
sudo ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

# Obtener certificado — sigue las instrucciones interactivas
sudo certbot --nginx -d n8n.tudominio.com

# La renovación automática ya está configurada por certbot
sudo systemctl status certbot.timer

Paso 6: Arrancar y verificar

bash
cd ~/n8n
docker compose up -d

# Ver logs
docker compose logs -f n8n

# Verificar que está escuchando
curl -I https://n8n.tudominio.com

Accede a https://n8n.tudominio.com — la primera vez te pide crear el usuario admin. Hazlo con email real (n8n permite recuperación) y password fuerte.

Paso 7: Backups automáticos

Sin backups, todo lo anterior es inútil el día que algo falle. La estrategia mínima viable: dump diario de PostgreSQL subido a almacenamiento externo (S3, Backblaze B2, R2 de Cloudflare).

bash
# /home/deploy/scripts/backup-n8n.sh
#!/bin/bash
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR=/home/deploy/backups

mkdir -p $BACKUP_DIR

# Dump de PostgreSQL
docker compose -f /home/deploy/n8n/docker-compose.yml exec -T postgres \
  pg_dump -U n8n n8n | gzip > "$BACKUP_DIR/n8n-$DATE.sql.gz"

# Subir a Backblaze B2 (o S3)
b2 upload-file mi-bucket "$BACKUP_DIR/n8n-$DATE.sql.gz" backups/

# Limpiar backups locales mayores de 7 días
find $BACKUP_DIR -type f -mtime +7 -delete

Programa con cron a las 3am cada día:

crontab
0 3 * * * /home/deploy/scripts/backup-n8n.sh >> /var/log/n8n-backup.log 2>&1
Un backup que no has restaurado nunca no es un backup — es una esperanza. Prueba la restauración al menos una vez al mes.

Operación día a día

Actualizar n8n a una versión nueva

bash
cd ~/n8n
docker compose pull n8n
docker compose up -d n8n
docker image prune -f

Ver consumo de recursos

bash
docker stats --no-stream

Restaurar desde backup

bash
gunzip -c n8n-20260505-030001.sql.gz | docker compose exec -T postgres psql -U n8n n8n
Coste real total mensual: 4,15€ VPS + 0€ certificado SSL + 1€ aprox. en backups B2 = ~5€/mes para infraestructura ilimitada de automatización.
n8n Docker Nginx PostgreSQL SSL DevOps Self-Hosted

¿Necesitas un n8n robusto en tu infraestructura?

Despliegue completo con backups, monitorización y workflows iniciales. Listo para producción en 48h en tu propio servidor.

Hablamos de tu setup →