Volver al blog
Aprendizajes13 ene 20268 min de lectura

Escala n8n: la guía definitiva del modo cola y Docker

Aprende a escalar eficazmente los flujos de trabajo de n8n usando el modo cola y Docker para optimizar el rendimiento y la fiabilidad en procesos de automatización.

Escrito por

Idir Ouhab Meskine

Actualizado el 30 mar 2026
Escala n8n: la guía definitiva del modo cola y Docker

¿Qué problema resolvemos?

Por defecto, n8n funciona como un único proceso que hace todo: muestra la interfaz, recibe webhooks, ejecuta workflows, gestiona triggers... Cuando tienes muchos workflows o tráfico alto, ese proceso único se convierte en un cuello de botella.

La solución es separar responsabilidades:

Componente¿Qué hace?
MainMuestra el editor, gestiona la API y los triggers (cron, polling)
WorkerEjecuta los workflows (el trabajo pesado)
Webhook ProcessorRecibe las peticiones HTTP de webhooks
Task RunnerEjecuta el código de los nodos Code (JS/Python) de forma aislada y segura
RedisCola de mensajes que conecta todo
PostgreSQLBase de datos compartida

Piensa en ello como un restaurante: el Main es el maître que recibe pedidos, Redis es la barra donde se dejan las comandas, el Worker es el cocinero, el Webhook Processor es la puerta de entrada para pedidos online y los Task Runners son los ayudantes especializados que preparan ingredientes específicos.


Arquitectura visual

                    ┌─────────────────┐
                    │  Reverse Proxy   │
                    │  (Nginx/Traefik) │
                    └────────┬────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
              ▼              │              ▼
      ┌──────────┐          │      ┌──────────────┐
      │   Main   │          │      │   Webhook    │
      │  Editor  │          │      │  Processor   │
      │  API     │          │      │  /webhook/*  │
      │  Triggers│          │      └──────┬───────┘
      └────┬─────┘          │             │
           │     ┌──────────┘             │
           │     │                        │
           ▼     ▼                        ▼
      ┌──────────────────────────────────────┐
      │              Redis (cola)            │
      └──────────────────┬───────────────────┘
                         │
                         ▼
                  ┌──────────┐
                  │  Worker  │
                  └────┬─────┘
                       │
                       ▼
                  ┌──────────┐
                  │  Runner  │
                  │ (sidecar)│
                  └──────────┘
                       │
                       ▼
              ┌─────────────────┐
              │   PostgreSQL    │
              └─────────────────┘

Requisitos previos

  • Un servidor Linux (Ubuntu 22.04+ recomendado)
  • Docker y Docker Compose instalados
  • Un dominio apuntando a tu servidor (para HTTPS)
  • Mínimo 4 GB RAM / 2 vCPU

Paso 1: Crear la estructura del proyecto

bash
mkdir n8n-scaling && cd n8n-scaling
touch docker-compose.yml .env

Paso 2: Configurar las variables de entorno

Crea tu archivo .env. Cambia las contraseñas por las tuyas:

env
    # General
N8N_VERSION=1.71.3
GENERIC_TIMEZONE=Europe/Berlin
N8N_ENCRYPTION_KEY=tu-clave-secreta-muy-larga-aqui

    # PostgreSQL
POSTGRES_USER=n8n
POSTGRES_PASSWORD=cambia-esta-password
POSTGRES_DB=n8n

    # Redis
REDIS_PASSWORD=cambia-esta-password-redis

    # n8n
N8N_HOST=n8n.tudominio.com
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.tudominio.com

    # Queue Mode
EXECUTIONS_MODE=queue
QUEUE_BULL_REDIS_HOST=redis
QUEUE_BULL_REDIS_PORT=6379
QUEUE_BULL_REDIS_PASSWORD=${REDIS_PASSWORD}

    # Database
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}

    # Task Runner
N8N_RUNNERS_MODE=external
N8N_RUNNERS_AUTH_TOKEN=tu-token-secreto-para-runners
OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true
️ Importante: El N8N_ENCRYPTION_KEY debe ser idéntico en todos los procesos (main, worker, webhook). Si no coincide, n8n no podrá leer las credenciales almacenadas.
Tip: Para generar claves seguras puedes usar: openssl rand -hex 32

Paso 3: Crear el docker-compose.yml

yaml
volumes:
  postgres_data:
  redis_data:
  n8n_data:

x-n8n-common: &n8n-common
  image: docker.n8n.io/n8nio/n8n:${N8N_VERSION}
  restart: unless-stopped
  environment: &n8n-env
    GENERIC_TIMEZONE: ${GENERIC_TIMEZONE}
    TZ: ${GENERIC_TIMEZONE}
    N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
    DB_TYPE: ${DB_TYPE}
    DB_POSTGRESDB_HOST: ${DB_POSTGRESDB_HOST}
    DB_POSTGRESDB_PORT: ${DB_POSTGRESDB_PORT}
    DB_POSTGRESDB_DATABASE: ${DB_POSTGRESDB_DATABASE}
    DB_POSTGRESDB_USER: ${DB_POSTGRESDB_USER}
    DB_POSTGRESDB_PASSWORD: ${DB_POSTGRESDB_PASSWORD}
    EXECUTIONS_MODE: ${EXECUTIONS_MODE}
    QUEUE_BULL_REDIS_HOST: ${QUEUE_BULL_REDIS_HOST}
    QUEUE_BULL_REDIS_PORT: ${QUEUE_BULL_REDIS_PORT}
    QUEUE_BULL_REDIS_PASSWORD: ${QUEUE_BULL_REDIS_PASSWORD}
    N8N_RUNNERS_MODE: ${N8N_RUNNERS_MODE}
    N8N_RUNNERS_AUTH_TOKEN: ${N8N_RUNNERS_AUTH_TOKEN}
  depends_on:
    postgres:
      condition: service_healthy
    redis:
      condition: service_healthy
  networks:
    - n8n-net

services:

  # ══════════════════════════════════════════════════════
  #  PostgreSQL
  # ══════════════════════════════════════════════════════
  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
    networks:
      - n8n-net

  # ══════════════════════════════════════════════════════
  #  Redis
  # ══════════════════════════════════════════════════════
  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: >
      redis-server
      --requirepass ${REDIS_PASSWORD}
      --maxmemory 256mb
      --maxmemory-policy allkeys-lru
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - n8n-net

  # ══════════════════════════════════════════════════════
  #  n8n Main
  # ══════════════════════════════════════════════════════
  n8n-main:
    <<: *n8n-common
    hostname: n8n-main
    environment:
      <<: *n8n-env
      N8N_HOST: ${N8N_HOST}
      N8N_PROTOCOL: ${N8N_PROTOCOL}
      WEBHOOK_URL: ${WEBHOOK_URL}
      OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS: ${OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS}
      N8N_RUNNERS_SERVER_URI: http://n8n-runner-main:5679
    ports:
      - "5678:5678"
    volumes:
      - n8n_data:/home/node/.n8n

  # Runner sidecar para Main
  n8n-runner-main:
    image: docker.n8n.io/n8nio/runners:${N8N_VERSION}
    restart: unless-stopped
    hostname: n8n-runner-main
    environment:
      N8N_RUNNERS_AUTH_TOKEN: ${N8N_RUNNERS_AUTH_TOKEN}
      N8N_RUNNERS_N8N_URI: http://n8n-main:5678
      N8N_RUNNERS_MAX_CONCURRENCY: 5
      GENERIC_TIMEZONE: ${GENERIC_TIMEZONE}
    depends_on:
      - n8n-main
    networks:
      - n8n-net

  # ══════════════════════════════════════════════════════
  #  Webhook Processor
  # ══════════════════════════════════════════════════════
  n8n-webhook:
    <<: *n8n-common
    hostname: n8n-webhook
    command: webhook
    environment:
      <<: *n8n-env
      WEBHOOK_URL: ${WEBHOOK_URL}
      N8N_RUNNERS_SERVER_URI: http://n8n-runner-webhook:5679
    ports:
      - "5680:5678"

  # Runner sidecar para Webhook
  n8n-runner-webhook:
    image: docker.n8n.io/n8nio/runners:${N8N_VERSION}
    restart: unless-stopped
    hostname: n8n-runner-webhook
    environment:
      N8N_RUNNERS_AUTH_TOKEN: ${N8N_RUNNERS_AUTH_TOKEN}
      N8N_RUNNERS_N8N_URI: http://n8n-webhook:5678
      N8N_RUNNERS_MAX_CONCURRENCY: 5
      GENERIC_TIMEZONE: ${GENERIC_TIMEZONE}
    depends_on:
      - n8n-webhook
    networks:
      - n8n-net

  # ══════════════════════════════════════════════════════
  #   Worker
  # ══════════════════════════════════════════════════════
  n8n-worker:
    <<: *n8n-common
    hostname: n8n-worker
    command: worker
    environment:
      <<: *n8n-env
      QUEUE_WORKER_CONCURRENCY: 10
      N8N_RUNNERS_SERVER_URI: http://n8n-runner-worker:5679

  # Runner sidecar para Worker
  n8n-runner-worker:
    image: docker.n8n.io/n8nio/runners:${N8N_VERSION}
    restart: unless-stopped
    hostname: n8n-runner-worker
    environment:
      N8N_RUNNERS_AUTH_TOKEN: ${N8N_RUNNERS_AUTH_TOKEN}
      N8N_RUNNERS_N8N_URI: http://n8n-worker:5678
      N8N_RUNNERS_MAX_CONCURRENCY: 5
      GENERIC_TIMEZONE: ${GENERIC_TIMEZONE}
    depends_on:
      - n8n-worker
    networks:
      - n8n-net

networks:
  n8n-net:
    driver: bridge

Paso 4: Entender cada componente

️PostgreSQL

La base de datos donde n8n guarda workflows, credenciales y ejecuciones. Es obligatorio para queue mode — SQLite no es compatible.

Redis

El mensajero que conecta todo. Cuando el main recibe un trigger o el webhook processor recibe una petición, no ejecutan el workflow directamente. En su lugar, ponen un mensaje en Redis diciendo "hay trabajo pendiente" y el worker lo recoge.

️Main

Sirve el editor visual, la API REST y gestiona los triggers (cron, polling, conexiones persistentes como IMAP o RabbitMQ). No ejecuta workflows — eso lo delega al worker a través de Redis.

Webhook Processor

Un proceso separado dedicado exclusivamente a recibir webhooks (/webhook/*). Se arranca con el comando webhook. Esto evita que un pico de tráfico en webhooks afecte al editor.

Worker

El que hace el trabajo pesado. Escucha la cola de Redis, toma ejecuciones pendientes y las procesa. QUEUE_WORKER_CONCURRENCY=10 significa que puede ejecutar hasta 10 workflows en paralelo.

Task Runners (Modo Externo)

A partir de n8n 2.0, los task runners vienen activados por defecto. Ejecutan el código de los nodos Code (JavaScript y Python) en un entorno aislado.

En modo externo (N8N_RUNNERS_MODE=external), cada proceso de n8n necesita su propio runner como sidecar — un contenedor compañero que corre a su lado. Por eso en el docker-compose ves tres runners: uno para el main, uno para el webhook y uno para el worker.

¿Por qué importa?

  • Seguridad: Un código con errores en un Code node no puede tumbar n8n.
  • Aislamiento: Los runners no tienen acceso a las variables de entorno ni al filesystem de n8n.
  • Estabilidad: Un script que consume mucha memoria se mata sin afectar a nada más.
Importante: La versión de la imagen n8nio/runners debe coincidir exactamente con la versión de n8nio/n8n.

Paso 5: Arrancar todo

bash
  # Levantar la infraestructura
docker compose up -d

  # Verificar que todo está corriendo
docker compose ps

  # Ver los logs en tiempo real
docker compose logs -f

Deberías ver algo como:

n8n-main       | n8n ready on 0.0.0.0, port 5678
n8n-webhook    | Webhook listener ready on 0.0.0.0, port 5678
n8n-worker     | Worker started successfully

Paso 6: Configurar el Reverse Proxy (Nginx)

Necesitas un reverse proxy para enrutar el tráfico correctamente:

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

    ssl_certificate     /etc/letsencrypt/live/n8n.tudominio.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.tudominio.com/privkey.pem;

    # Webhooks de producción → Webhook Processor
    location /webhook/ {
        proxy_pass http://127.0.0.1:5680;
        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_buffering off;
    }

    # Webhooks de test → Main (botón "Test workflow")
    location /webhook-test/ {
        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;
    }

    # Todo lo demás (editor, API) → Main
    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;
        proxy_set_header Connection '';
        proxy_http_version 1.1;
        proxy_buffering off;
    }
}

La regla clave: /webhook/* va al puerto 5680 (webhook processor) y todo lo demás al 5678 (main).


Paso 7: Verificar que todo funciona

  • [ ] docker compose ps — todos los contenedores en "running"
  • [ ] Accedes al editor en https://n8n.tudominio.com
  • [ ] Crear y guardar un workflow funciona
  • [ ] Un webhook trigger recibe peticiones externas
  • [ ] El botón "Test workflow" funciona desde el editor
  • [ ] Un Code node ejecuta código sin errores (runner funcionando)

Errores comunes

"Could not find encryption key" — El N8N_ENCRYPTION_KEY no es el mismo en todos los servicios. Revisa tu .env.

Los webhooks no llegan — Verifica que tu reverse proxy enruta /webhook/* al puerto 5680, no al 5678.

El worker no procesa nada — Revisa que puede conectarse a Redis y PostgreSQL: docker compose logs n8n-worker.

Task runner "unhealthy" — La versión de n8nio/runners no coincide con n8nio/n8n. Deben ser idénticas.


Resumen de variables clave

VariableValorDescripción
EXECUTIONS_MODEqueueActiva queue mode
N8N_ENCRYPTION_KEY(tu clave)Idéntica en TODOS los procesos
N8N_RUNNERS_MODEexternalTask runners en contenedores separados
N8N_RUNNERS_AUTH_TOKEN(tu token)Autenticación entre n8n y runners
OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERStrueEjecuciones manuales van al worker
QUEUE_WORKER_CONCURRENCY10Ejecuciones paralelas del worker
WEBHOOK_URLhttps://tu.dominioURL pública para webhooks

¿Cuándo necesitas esto?

  • < 1.000 ejecuciones/día: n8n normal con PostgreSQL es suficiente.
  • 1.000 - 10.000 ejecuciones/día: Queue mode con 1 worker.
  • > 10.000 ejecuciones/día: Añade más workers y webhook processors.
  • Ejecutas código en Code nodes: Task runners en modo externo (obligatorio en n8n 2.0+).

Recursos

Temas

n8nmodo coladockerautomatización de flujosescaladoherramientas de automatizaciónnubecontenedoresoptimización de rendimiento

Sigue leyendo

Vuelve al archivo y lee más artículos.

Ver todos los artículos
Escala n8n: la guía definitiva del modo cola y Docker | Blog