SSD Nodes Learn 🎉 VPS desde $5.50/mes
Guías Matt ConnorPor Matt Connor

Por qué n8n se desconecta en tu VPS

El aviso de desconexión de n8n puede ser websocket, reinicios, falta de memoria o una programación inactiva. Aprende a distinguirlos con Docker y el VPS.

Por qué n8n se desconecta: cuatro fallos y un solo síntoma

«n8n se desconecta» es una sola frase que puede describir cuatro fallos diferentes, y cada uno requiere una solución distinta. El editor muestra un aviso de pérdida de conexión mientras el contenedor sigue funcionando con normalidad. El contenedor se reinicia solo. El kernel termina el proceso Node.js porque consume demasiada memoria. O el proceso no tiene ningún problema y, simplemente, un flujo de trabajo activo nunca se ejecuta. Si cambia el ajuste equivocado, pasará todo un fin de semana resolviendo un problema que no existía.

Por tanto, determine qué fallo tiene antes de modificar la configuración. n8n se ejecuta como un único proceso Node.js, normalmente dentro de un contenedor Docker y detrás de un reverse proxy que termina TLS (seguridad de la capa de transporte). Cada capa puede fallar de una forma distinta, pero el navegador informa de todas con el mismo mensaje.

Diagnostique en este orden

Ejecute estos comandos en el VPS (servidor privado virtual) y lea los valores que muestra su propia máquina. No los compare con números de una publicación de un foro. Los valores relevantes describen su sistema, no el de otra persona.

docker ps -a --filter name=n8n
docker logs --tail 200 --timestamps n8n
docker inspect n8n | grep -iE 'Status|Running|RestartCount|OOMKilled|ExitCode'
docker stats --no-stream

La columna STATUS de docker ps -a indica cuánto tiempo lleva el contenedor en su estado actual. Compare ese tiempo con el momento en que comenzó el problema. Si el contenedor lleva activo desde mucho antes de que apareciera el aviso, n8n nunca estuvo fuera de servicio. Lo que falló es la conexión entre el navegador y el backend. Esta conexión usa la ruta de websocket que se explica en la sección siguiente.

RestartCount indica cuántas veces Docker ha reiniciado este contenedor. Anote el número, espere un minuto y vuelva a leerlo. Si el número aumenta mientras lo observa, existe un bucle de reinicios. Las líneas del registro justo antes de cada reinicio indican la causa.

OOMKilled es un indicador con valor verdadero o falso. True significa que el kernel de Linux terminó el proceso porque superó un límite de memoria, ya fuera el límite propio del contenedor o el de toda la máquina. Este campo permite distinguir un cierre por falta de memoria de cualquier otro tipo de salida. Por eso debe leerlo antes de hacer suposiciones.

ExitCode es el código con el que terminó el contenedor la última vez. No necesita memorizar el significado de cada código. Lea el suyo y, después, revise el final de docker logs correspondiente a la misma marca de tiempo. El final del registro y el indicador de falta de memoria, considerados conjuntamente, muestran lo que ocurrió. Cualquiera de los dos por separado puede llevar a una conclusión incorrecta.

docker stats muestra el uso de memoria actual junto al límite aplicado. Déjelo ejecutándose en un segundo terminal, active el flujo de trabajo que provoca el problema y observe cómo cambia el valor mientras se produce el fallo.


El banner de conexión perdida suele indicar un problema en el reverse proxy

El editor de n8n mantiene abierta una conexión push de larga duración con el backend para transmitir el progreso de las ejecuciones al lienzo. De forma predeterminada, esa conexión es un WebSocket, que es lo que selecciona N8N_PUSH_BACKEND, y su valor predeterminado es websocket. Un WebSocket comienza como una solicitud HTTP normal que incluye las cabeceras Connection: Upgrade y Upgrade: websocket. El servidor responde 101 Switching Protocols y, a partir de ese momento, ambos extremos utilizan el mismo socket TCP en las dos direcciones.

Hay dos problemas que pueden interrumpir este proceso, y ambos se producen en el proxy, no en n8n. El proxy usa HTTP/1.0 hacia el backend o elimina las cabeceras de actualización, por lo que la actualización nunca se completa y el editor intenta conectarse de nuevo de forma indefinida. También puede ocurrir que la actualización se complete y que el proxy cierre posteriormente el socket porque ha permanecido inactivo. Un WebSocket sin mensajes parece exactamente una conexión inactiva. En ambos casos, el contenedor funciona correctamente. El banner indica que el navegador ha perdido su canal de comunicación.

Confírmelo en el navegador antes de modificar nada. Abra las herramientas de desarrollo, vaya a la pestaña Network, filtre por WS y vuelva a cargar el editor. La solicitud push debería llegar a 101 Switching Protocols y permanecer abierta. Si la solicitud push devuelve un código de estado normal o vuelve a aparecer cada pocos segundos, el problema está en el proxy.

Los ajustes de nginx que mantienen conectado el editor

nginx no reenvía una actualización a menos que se lo indique. proxy_pass usa HTTP/1.0 para comunicarse con el backend de forma predeterminada, y Connection y Upgrade son cabeceras salto a salto que nginx elimina durante el reenvío. Debe volver a incluir ambas. El bloque map va en el contexto http, no dentro de server.

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
server {
    listen 443 ssl;
    http2 on;
    server_name n8n.example.com;

    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $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_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
    }
}

proxy_read_timeout es la línea que se suele omitir. Su valor predeterminado es 60 segundos y también se aplica a un WebSocket actualizado, por lo que una pestaña del editor que permanece abierta en una instancia sin actividad pierde la conexión aproximadamente un minuto después de que se haya transmitido el último mensaje. Aumentar este valor corrige el aviso que aparece al volver a una pestaña que se dejó abierta.

sudo nginx -t && sudo systemctl reload nginx
sudo nginx -T | grep -iE 'proxy_http_version|upgrade|proxy_read_timeout'

nginx -T muestra la configuración completa en ejecución, no un solo archivo, por lo que confirma que la edición se ha cargado realmente. Si la configuración está en un archivo que ninguna línea include incluye, la corrección correcta parece no tener efecto.

A continuación, indique a n8n que está detrás de un proxy, porque genera las URL a partir de estos valores.

environment:
  - N8N_HOST=n8n.example.com
  - N8N_PROTOCOL=https
  - N8N_PORT=5678
  - N8N_PROXY_HOPS=1
  - N8N_WEBHOOK_URL=https://n8n.example.com/

N8N_PROXY_HOPS tiene el valor predeterminado 0, lo que significa que n8n trata la dirección de conexión como la dirección del cliente e ignora X-Forwarded-For. Establézcalo en el número de proxies situados delante del contenedor. En agosto de 2026, N8N_WEBHOOK_URL es el nombre actual y WEBHOOK_URL, el nombre anterior, todavía funciona, aunque muestra un aviso de obsolescencia durante el arranque.

Traefik reenvía WebSocket, pero después agota el tiempo de espera

Traefik reenvía una solicitud de actualización de WebSocket sin middleware ni etiquetas adicionales. Por eso, cuando un usuario de Traefik ve este mensaje, normalmente se enfrenta a un tiempo de espera agotado y no a la ausencia de una cabecera. Estos parámetros se configuran en el entryPoint. En agosto de 2026, en Traefik v3, idleTimeout tiene un valor predeterminado de 180 segundos y readTimeout tiene un valor predeterminado de 60 segundos.

entryPoints:
  websecure:
    address: ":443"
    transport:
      respondingTimeouts:
        readTimeout: 0
        idleTimeout: 3600s

Caddy gestiona la actualización automáticamente en reverse_proxy y no necesita ninguna directiva para ello. Si no puede cambiar el proxy porque lo administra otra persona, cambie el canal de inserción mediante N8N_PUSH_BACKEND=sse. SSE (eventos enviados por el servidor) es una respuesta HTTP normal que permanece abierta. Por eso funciona con un proxy que rechaza las actualizaciones, aunque un tiempo de espera de inactividad demasiado agresivo también puede interrumpirla. Elegir el proxy es una decisión independiente. La comparación entre nginx, Caddy y Traefik explica el coste operativo de cada opción.

Cuando el contenedor realmente se está reiniciando

Si RestartCount aumenta, el contenedor está fallando y Docker lo vuelve a iniciar. Compare las marcas de tiempo del registro con cada reinicio y lea lo que aparece justo antes. Cuatro causas explican casi todos los casos: un error de configuración que impide el arranque, una base de datos a la que n8n no puede conectarse, un bloqueo cuando el servicio ya está ejecutándose y una terminación por falta de memoria.

Empiece por el volumen, porque los permisos son la causa menos evidente. La imagen oficial se ejecuta con el usuario sin privilegios node y guarda sus datos en /home/node/.n8n. Un bind mount creado por root no permite escribir a ese usuario. Por eso el proceso termina en cada arranque y la política de reinicio lo oculta detrás de un bucle.

docker compose config
docker run --rm -it --entrypoint sh docker.n8n.io/n8nio/n8n -c 'id'
docker exec n8n ls -ld /home/node/.n8n

Un volumen con nombre evita por completo el problema, porque Docker lo crea con el propietario correcto. Si necesita un bind mount, chown el directorio del host al ID de usuario numérico que mostró el primer comando. Conviene entender una vez cómo se asignan los propietarios entre el host y el contenedor. La explicación sobre PUID y PGID describe cómo estas imágenes determinan quién puede escribir los archivos.

El kill por falta de memoria que parece un fallo

Hay dos límites de memoria independientes por encima de un proceso de n8n, y fallan de forma distinta. El límite del cgroup del contenedor lo aplica el kernel: si se supera, el proceso se termina de inmediato, sin posibilidad de escribir nada, y OOMKilled devuelve true. El límite del heap de V8 lo aplica Node.js internamente: si se supera, Node genera un error de heap con un stack trace y se cierra por su cuenta, por lo que OOMKilled devuelve false. Desde el navegador, ambos casos parecen iguales. En docker inspect, están separados por un campo.

Establezca el límite del heap de Node por debajo del límite del contenedor. Si el límite del heap es el mayor de los dos, V8 sigue asignando memoria después de que el kernel debería intervenir. Por eso, su recolector de basura nunca alcanza su propio límite y siempre se produce el fallo más grave, sin ningún registro que revisar.

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    environment:
      - NODE_OPTIONS=--max-old-space-size=<MiB, below the container limit>
    deploy:
      resources:
        limits:
          memory: <your container limit>

Elija ambos valores según los recursos reales de su VPS y deje margen para la base de datos, el proxy y el sistema operativo. docker stats --no-stream muestra el uso actual junto al límite aplicado, de modo que puede comprobar que el límite que escribió es el que Docker aplicó. Cómo se aplican los límites de memoria de Compose explica qué clave tiene prioridad cuando se configuran varias.

Los datos de ejecución crecen debajo de usted

Una ejecución contiene la salida de cada nodo mientras el flujo está en curso, y n8n almacena esos datos. De aquí se derivan dos consecuencias. La memoria máxima de una ejecución depende del lote de datos más grande que procese, por lo que un flujo que maneja diez mil filas de una vez es un programa distinto del mismo flujo cuando maneja doscientas cada vez. Además, la copia almacenada sigue creciendo hasta que algo la elimina.

La limpieza automática resuelve el segundo problema. En agosto de 2026, los valores predeterminados son la limpieza habilitada, EXECUTIONS_DATA_MAX_AGE en 336 horas (14 días) y EXECUTIONS_DATA_PRUNE_MAX_COUNT en 10000. Son valores holgados para un VPS pequeño que ejecuta SQLite, donde un solo archivo contiene todos los datos y el mismo proceso que sirve el editor debe leerlos y escribirlos.

environment:
  - EXECUTIONS_DATA_PRUNE=true
  - EXECUTIONS_DATA_MAX_AGE=72
  - EXECUTIONS_DATA_PRUNE_MAX_COUNT=1000
  - EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
  - EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=false

EXECUTIONS_DATA_SAVE_ON_SUCCESS=none es el ajuste agresivo. Conserva las ejecuciones fallidas para depurarlas y elimina las correctas. Decídalo de forma explícita, porque si un flujo produce una salida incorrecta sin generar un error, después no tendrá nada que inspeccionar. La limpieza también marca primero las filas como eliminadas y las borra en una pasada posterior. Además, SQLite reutiliza las páginas liberadas en lugar de devolverlas, por lo que el archivo del disco no se reduce en cuanto cambia el ajuste.

Para reducir el pico en lugar del total almacenado, mueva menos datos en cada ejecución. Divida los trabajos grandes en subflujos de trabajo que devuelvan resultados pequeños al flujo principal, agrupe los elementos con el nodo Loop Over Items y mantenga los conjuntos de datos completos fuera del nodo Code.

Los archivos binarios no deben permanecer en memoria

N8N_DEFAULT_BINARY_DATA_MODE tiene el valor predeterminado default, que mantiene los datos binarios en la memoria de la ejecución en curso. Cada archivo que descarga un nodo y cada copia que se entrega al nodo siguiente permanecen allí hasta que termina la ejecución. Un solo flujo de trabajo que descargue varios archivos adjuntos grandes puede hacer que el proceso supere un límite que las operaciones normales con JSON nunca alcanzan. Por eso el bloqueo afecta a un flujo de trabajo concreto y no depende del tiempo transcurrido.

environment:
  - N8N_DEFAULT_BINARY_DATA_MODE=filesystem

Con filesystem, los datos binarios se escriben en N8N_BINARY_DATA_STORAGE_PATH. De forma predeterminada, esta ruta se encuentra dentro de la carpeta del usuario de n8n y, por tanto, usa el mismo volumen que el resto de los datos. Compruebe que haya espacio disponible en el volumen antes de cambiar esta opción. N8N_PAYLOAD_SIZE_MAX establece el tamaño máximo de la carga útil entrante de un webhook en MiB (mebibytes) y tiene el valor predeterminado 16. Aumentarlo permite recibir solicitudes más grandes, pero implica aceptar un mayor consumo de memoria.

Todo lo demás que comparta el servidor compite por la misma RAM. Si los procesos OOM killer empezaron cuando añadió un contenedor de base de datos, ejecutar la base de datos en Docker o en el host es la decisión que ahora debe tomar.

Política de reinicio y recuperación tras un reinicio

Un contenedor sin una política de reinicio permanece detenido después de salir y también después de que se reinicie el host. restart: unless-stopped lo inicia de nuevo en ambos casos y respeta un contenedor que haya detenido manualmente. restart: always también reinicia un contenedor que haya detenido deliberadamente cuando Docker vuelva a iniciarse.

n8n ofrece un endpoint de comprobación de estado, cuyo nombre indica N8N_ENDPOINT_HEALTH y cuyo valor predeterminado es healthz. Compruébelo primero desde el host para confirmar que la ruta es correcta en su instancia.

curl -fsS http://127.0.0.1:5678/healthz
docker exec n8n which wget curl
sudo systemctl is-enabled docker

Una comprobación de estado por sí sola no reinicia nada. Compose marca el contenedor como no saludable y no hace nada más. Por tanto, la comprobación necesita una política de reinicio o un monitor externo junto a ella para tener algún efecto. Cómo escribir una comprobación de estado que actúe realmente y Cómo hacer que la pila vuelva a iniciarse después de un reinicio cubren ambas partes.

El flujo de trabajo que nunca se ejecuta aunque n8n funcione correctamente

Este caso no muestra ningún aviso ni provoca ningún reinicio. El contenedor está activo, el editor funciona y la ejecución esperada no aparece en la lista de ejecuciones. En la mayoría de los casos, la causa es una de estas cuatro:

  • El flujo de trabajo no está activo. Un Schedule Trigger sólo se ejecuta en la ruta de producción, por lo que probarlo en el lienzo no programa ninguna ejecución.
  • La zona horaria no es la suya. GENERIC_TIMEZONE usa America/New_York de forma predeterminada, por lo que una programación establecida para las 09:00 se ejecuta a las 09:00 en esa zona hasta que configure GENERIC_TIMEZONE y TZ con su propia zona horaria.
  • El tiempo de inactividad no se recupera después. Los triggers se registran cuando n8n se inicia, por lo que una programación cuyo momento llegó mientras el contenedor se reiniciaba no se ejecuta con retraso. La siguiente ejecución será la próxima hora programada después del arranque.
  • El flujo de trabajo se desactivó automáticamente. N8N_WORKFLOW_AUTODEACTIVATION_ENABLED está desactivado de forma predeterminada. Cuando se activa, un flujo de trabajo que sigue fallando se despublica. Después, parece exactamente uno que nunca se activó.

Abra la lista de ejecuciones y filtre por ese flujo de trabajo. Una entrada con error indica un problema del flujo de trabajo. La ausencia total de entradas indica un problema del trigger. Revise las cuatro causas anteriores.

Qué cambiar primero

  1. Lea STATUS, RestartCount y OOMKilled en su propio contenedor antes de editar cualquier archivo.
  2. Si el contenedor nunca se detuvo, corrija las cabeceras de actualización del proxy y el tiempo de espera de inactividad.
  3. Si OOMKilled es verdadero, establezca deliberadamente un límite para el contenedor, configure el límite máximo del heap de Node por debajo de ese valor y cambie los datos binarios a filesystem.
  4. Si no se activó nada, compruebe que el flujo de trabajo esté activo y que la zona horaria de la instancia sea la suya.

La mayor parte de esto es configuración que se establece una vez y luego no requiere más cambios, sobre una instalación funcional. Si todavía está preparando esa instalación, la guía para ejecutar n8n en Docker con HTTPS es la base a la que pertenecen estos ajustes.

FAQ

¿Por qué el editor de n8n muestra un aviso de pérdida de conexión cuando el contenedor está en ejecución?

El editor mantiene abierto un WebSocket para transmitir el progreso de las ejecuciones. Si el proxy inverso no reenvía las cabeceras Connection: Upgrade y Upgrade: websocket, o no usa HTTP/1.1 hacia el upstream, la actualización nunca se completa y el navegador intenta conectarse de nuevo continuamente mientras n8n sigue funcionando correctamente. En nginx necesita proxy_http_version 1.1 y las dos líneas proxy_set_header, además de un proxy_read_timeout superior a los 60 segundos predeterminados para que una pestaña inactiva no se desconecte. Compruebe la configuración activa con sudo nginx -T, no el archivo que editó.

¿Cómo distingo una terminación por falta de memoria de un fallo normal?

Ejecute docker inspect n8n | grep -iE 'OOMKilled|ExitCode|RestartCount' y consulte el indicador OOMKilled. El valor True significa que el kernel terminó el proceso porque superó un límite de memoria. En el registro del contenedor no habrá información útil porque el proceso no tuvo oportunidad de escribirla. El valor False, junto con un error de heap y un stack trace al final de docker logs, significa que Node.js alcanzó su propio límite de heap de V8 y terminó por sí mismo. Establezca NODE_OPTIONS=--max-old-space-size por debajo del límite del contenedor para obtener el segundo tipo de fallo, que es el que deja evidencias.

¿La depuración de los datos de ejecución libera espacio en disco de inmediato?

No. EXECUTIONS_DATA_PRUNE marca las ejecuciones antiguas para eliminarlas y una pasada posterior las elimina, según la programación definida por EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL. Con SQLite, el archivo también reutiliza las páginas liberadas en lugar de devolverlas al sistema de archivos, por lo que el tamaño en disco permanece estable durante un tiempo después de eliminar las filas. Establezca EXECUTIONS_DATA_MAX_AGE y EXECUTIONS_DATA_PRUNE_MAX_COUNT con valores adecuados para su servidor y vuelva a comprobarlo al día siguiente, no inmediatamente.

¿Por qué no se ejecutó mi flujo de trabajo programado mientras n8n se reiniciaba?

n8n registra los triggers cuando se inicia el proceso y no vuelve a ejecutar las programaciones cuyo momento llegó mientras estaba detenido. Por tanto, un ciclo de reinicios produce silencio en lugar de una ráfaga de ejecuciones pendientes, y la siguiente ejecución será la próxima hora programada después del inicio. Si necesita ejecuciones que no se puedan perder, haga que un cliente externo active el flujo mediante un webhook, de modo que la lógica de reintento quede fuera de n8n.

¿Un healthcheck reiniciará n8n cuando deje de responder?

No por sí solo. Un healthcheck de Compose sólo marca el contenedor como healthy o unhealthy. El reinicio corresponde a la política de reinicio, por lo que restart: unless-stopped vuelve a iniciar el contenedor después de que termine y también lo inicia después de reiniciar el host, siempre que el servicio de Docker esté habilitado. Confírmelo con sudo systemctl is-enabled docker. Para actuar específicamente cuando el estado sea unhealthy, necesita un watcher externo a Docker que lea el estado y reinicie el servicio.