Saltar al contenido
Gontzal Bilbao
← blog

n8n en local con Docker, paso a paso

Instalo n8n en Windows con Docker y persistencia, comparo docker run con Compose y pruebo un primer webhook con un POST real.

serie · n8n y LinkedIn
n8n en local con Docker, paso a paso

El problema

Dejé n8n corriendo en mi Windows con Docker, con los workflows y las credenciales a salvo en un volumen. Un primer webhook responde a un POST lanzado desde PowerShell.

Quería una base local para automatizar sin pagar un servicio ni montar un servidor. La primera prueba la hice sin volumen y perdí la cuenta al borrar el contenedor.

Este post es la base de la serie. El caso real que se apoya en ella está en Auto-publicar el blog en LinkedIn con GitHub Actions y n8n.

idea clave

El contenedor se puede tirar cuando quieras. Lo que importa vive en el volumen.

Qué necesito

  • Docker Desktop arrancado en Windows.
  • PowerShell.
  • El puerto 5678 libre, que es el que usa n8n por defecto.

n8n guarda su estado en /home/node/.n8n dentro del contenedor: la base de datos SQLite, los workflows, las credenciales y la clave con que las cifra. Ese directorio es el que monto en un volumen.

Arrancar con docker run

Para comprobar que n8n arranca en mi equipo, un solo comando basta:

# PowerShell
docker run -d `
  --name n8n-local `
  -p 5678:5678 `
  -v n8n_data:/home/node/.n8n `
  -e TZ=Europe/Madrid `
  docker.n8n.io/n8nio/n8n:latest

-v n8n_data:... crea un volumen con nombre gestionado por Docker. TZ fija la zona horaria, que importa cuando un workflow se programa por hora.

# PowerShell: estado y logs (Ctrl + C sale sin parar el contenedor)
docker ps
docker logs -f n8n-local

Con el contenedor en marcha, abro http://localhost:5678 y creo la cuenta de owner. Esa cuenta ya queda en el volumen.

Pasar a Compose sin perder nada

docker run sirve para probar. Para el día a día prefiero Compose: la configuración queda en un fichero, y arrancar o recrear es un comando corto.

Creo una carpeta fuera de cualquier repo, por ejemplo C:\n8n-local, y dentro este fichero:

# docker-compose.yml
services:
  n8n:
    image: docker.n8n.io/n8nio/n8n:latest
    container_name: n8n-local
    restart: unless-stopped
    ports:
      - "5678:5678"
    environment:
      TZ: Europe/Madrid
      N8N_HOST: localhost
      N8N_PORT: 5678
      N8N_PROTOCOL: http
    volumes:
      - n8n_data:/home/node/.n8n

volumes:
  n8n_data:
    external: true
    name: n8n_data

El bloque external: true es lo que me dejó migrar sin perder nada. Le dice a Compose que use el volumen que ya creó docker run, en vez de crear otro.

ojo

Sin external: true, Compose crea un volumen con el nombre del proyecto delante, como n8n-local_n8n_data. n8n arranca vacío y parece que se ha borrado todo.

Antes de levantarlo, quito el contenedor de la prueba. El volumen no se toca:

# PowerShell, en C:\n8n-local
docker rm -f n8n-local
docker compose up -d
docker compose ps
docker compose logs -f n8n

Si empiezo directamente con Compose, sin pasar por docker run, quito external: true y name. Compose crea el volumen la primera vez.

Para comprobar la persistencia, guardo un workflow vacío, ejecuto docker compose down y docker compose up -d. Si el workflow sigue ahí, el volumen funciona.

Un primer workflow con webhook

Para entender cómo pasan los datos entre nodos, monto el flujo más corto que recibe algo y contesta:

Webhook → Edit Fields → Respond to Webhook

  1. Webhook: método POST, path demo-n8n-local. En Respond, elijo Using 'Respond to Webhook' Node.
  2. Edit Fields (opcional): añado received y source para ver cómo se transforma la entrada.
  3. Respond to Webhook: respondo con JSON.

Si Respond se queda en Immediately, el webhook contesta antes de llegar al último nodo. n8n lo avisa con un warning en Respond to Webhook.

El cuerpo de la respuesta tiene que ser JSON válido, con claves entre comillas. Este es el que uso:

{
  "ok": true,
  "message": "Webhook recibido en local"
}

Probarlo desde PowerShell

Guardo el workflow y pulso Listen for test event en el nodo Webhook. Mientras escucha, lanzo el POST:

# PowerShell: POST a la URL de prueba
$body = @{
  event   = "test_local"
  source  = "powershell"
  message = "Hola desde Windows"
} | ConvertTo-Json

$response = Invoke-WebRequest `
  -UseBasicParsing `
  -Method Post `
  -Uri "http://localhost:5678/webhook-test/demo-n8n-local" `
  -ContentType "application/json" `
  -Body $body

$response.StatusCode
$response.Content
# salida esperada
200
{"ok":true,"message":"Webhook recibido en local"}

Uso Invoke-WebRequest y no Invoke-RestMethod porque quiero ver el código HTTP, no solo el cuerpo. -UseBasicParsing evita el aviso de Windows PowerShell 5.1.

En la UI aparece la ejecución, con el payload de entrada y la salida de cada nodo. Ahí es donde depuro.

webhook-test o webhook

Cada nodo Webhook tiene dos URL, y confundirlas fue mi primer «no funciona»:

URLCuándo responde
/webhook-test/demo-n8n-localSolo mientras el editor está en Listen for test event
/webhook/demo-n8n-localSolo con el workflow publicado (activo)

En las versiones recientes, activar es el botón Publish; en las anteriores, el interruptor Active. Con el workflow publicado, repito el POST cambiando webhook-test por webhook y debe volver el mismo 200.

La URL de producción es la que importa para automatizar. Ningún sistema externo va a pulsar un botón en mi editor.

Si algo falla

  • localhost:5678 no abre: Docker Desktop parado, contenedor caído o puerto ocupado. Mira docker ps.
  • Workflows perdidos tras reiniciar: falta el volumen, o Compose creó uno nuevo.
  • 404 en el webhook: URL de test sin escucha, URL de producción sin publicar o path distinto.
  • No responde lo esperado: Respond sigue en Immediately, o el método no es POST.
  • Cambié el YAML y no se nota: docker compose up -d --force-recreate.

Lo que aprendí

El volumen, antes que nada: la cuenta y las credenciales de n8n viven ahí, no en el contenedor. Y la mitad de mis «el webhook no funciona» eran la URL de test sin el editor escuchando.

¿Te ha servido?

Si tienes dudas o lo estás montando tú, cuéntamelo.

Escríbeme Compartir en LinkedIn

sigue leyendo

Auto-publicar el blog en LinkedIn con GitHub Actions y n8n

El MVP con el que cada post nuevo salía en LinkedIn: tras el deploy, GitHub Actions avisa a n8n, que valida un token y publica.

3 mar · 5 minautomatización

Pipeline CI/CD en AWS con rollback automático

Un pipeline en AWS que despliega por fases, vigila error rate y latencia, y vuelve solo a la versión anterior si se supera un umbral.

10 ene · 5 minautomatización