Estamos ejecutando una instancia de n8n alojada en Railway y hemos estado experimentando problemas de rendimiento cada vez mayores a medida que nuestro uso ha crecido.
Actualmente procesamos más de 40 000 ejecuciones de flujo de trabajo por mes.
El problema principal es que los flujos de trabajo que normalmente deberían completarse en aproximadamente 1 segundo ahora tardan frecuentemente 3–4 segundos en terminar, a pesar de que la lógica del flujo de trabajo en sí no ha cambiado.
También estamos viendo que se forman colas de ejecución aleatoriamente. Esto no debería suceder según nuestra configuración actual, ya que no hemos configurado intencionalmente ningún límite de concurrencia.
Más recientemente, también hemos comenzado a ver este error:
Esta ejecución falló en ser procesada demasiadas veces y ya no volverá a intentarlo. Para permitir que esta ejecución se complete, desglose su flujo de trabajo o aumente sus workers o ajuste la configuración de sus workers.
Honestamente, no sabemos qué más intentar. Las métricas de Railway para nuestros workers, instancia primaria y PostgreSQL se ven saludables, sin cuellos de botella de recursos obvios. ¿Alguien ha experimentado algo similar o tiene alguna idea sobre qué deberíamos investigar a continuación?
¿Cuál es el mensaje de error (si hay alguno)?
Por favor, comparta su flujo de trabajo
(Seleccione los nodos en su lienzo y use los atajos de teclado CMD+C/CTRL+C y CMD+V/CTRL+V para copiar y pegar el flujo de trabajo.)
Comparta el resultado devuelto por el último nodo
Información sobre su configuración de n8n
Versión de n8n: 2.28.4
Base de datos: PostgreSQL
Configuración de n8n EXECUTIONS_PROCESS (predeterminada: own, main):
Ejecutar n8n mediante (Docker, npm, n8n cloud, aplicación de escritorio): railway
Según los síntomas y el mensaje de error que ves, es probable que tu instancia de n8n esté sufriendo por Sobrecarga de Procesos y Inflado de Base de Datos, que son comunes cuando las instancias auto-alojadas escalan hacia 40k+ ejecuciones por mes.
El error “This execution failed to be processed too many times” ocurre típicamente cuando una ejecución es recogida por un worker, pero el worker falla al “registrarse” o terminar antes de que expire el bloqueo. El sistema asume que el worker se bloqueó e reintenta el trabajo hasta alcanzar el límite.
Mencionaste la configuración EXECUTIONS_PROCESS. Si estás usando el modo own predeterminado, n8n genera un nuevo proceso de Node.js para cada ejecución individual.
El Problema: Esto añade una sobrecarga significativa (CPU y RAM) y crea un retraso de “arranque en frío” de 1–3 segundos para cada flujo de trabajo. Conforme tu uso crece, esto ejerce una presión inmensa en el planificador del SO y la memoria.
La Solución: Cambia tu variable de entorno a: EXECUTIONS_PROCESS=main
Si estás ejecutando n8n en Modo de Cola (usando workers separados), probablemente estés alcanzando un tiempo de espera de bloqueo. Incluso si un flujo de trabajo tarda solo 4 segundos, la contención de la base de datos o la inestabilidad de la red en Railway puede causar que el “latido” falle.
La Solución: Aumenta la duración del bloqueo para dar más espacio a los workers. Añade esta variable de entorno: QUEUE_WORKER_LOCK_DURATION=120000 (Esto aumenta el bloqueo de 60s a 120s).
Las métricas de Railway muestran CPU/RAM, pero no muestran inflado de tablas de PostgreSQL. Con 40,000+ ejecuciones/mes, tu tabla execution_entity puede crecer enormemente, ralentizando las mismas consultas que n8n usa para gestionar la cola.
La Investigación: Comprueba si tienes la poda de ejecuciones habilitada. Si la base de datos está inflada, incluso las métricas de CPU “saludables” no te salvarán de la lentitud de I/O.
La Solución: Asegúrate de que estas variables estén configuradas para mantener tu base de datos limpia:
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=168 (Poda datos más antiguos que 7 días; ajusta según sea necesario).
EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000 (Limita el total de registros).
Ya que no has establecido límites de concurrencia, una ráfaga repentina de webhooks puede desencadenar docenas de procesos simultáneos, causando las “colas aleatorias” y caídas de rendimiento conforme el sistema se agota.
La Solución: Establece un techo global para proteger tu instancia: N8N_CONCURRENCY_PRODUCTION_LIMIT=10 (Comienza con 10 y aumenta si tus recursos en Railway lo permiten).
Ya estamos ejecutando en Queue Mode, y ahora hemos aplicado casi todas tus sugerencias.
Ya teníamos Queue Mode configurado.
Hemos habilitado la poda de ejecuciones (EXECUTIONS_DATA_PRUNE, EXECUTIONS_DATA_MAX_AGE, etc.).
También hemos aumentado la duración del bloqueo del trabajador.
La única sugerencia que no aplicamos fue EXECUTIONS_PROCESS=main, porque según lo que entendemos, esta configuración está deprecada en versiones recientes de n8n y no es aplicable cuando se usa Queue Mode.
Por el momento no hemos tenido la oportunidad de validar si estos cambios mejoraron la situación, ya que la degradación del rendimiento ocurre de forma intermitente. Estamos esperando el próximo incidente para ver si el problema vuelve a aparecer.
Has hecho las cosas correctas — modo de cola, poda, el bump de bloqueo — y tienes razón en que EXECUTIONS_PROCESS está deprecado (el modo propio-proceso se eliminó; en n8n moderno es todo principal o cola/worker, así que ese fue un no-op para ti). El problema es que cambiaste cuatro variables a la vez sin línea base, así que incluso si mejoró, no puedes saber cuál fue el responsable. Antes de ajustar más, mide — aquí te explico cómo identificar cuál de los tres cuellos de botella habituales tienes: E/S de BD, contención de workers o un workflow que se filtra.
Habilitar la poda no recupera lo que ya está allí.EXECUTIONS_DATA_PRUNE solo detiene que nuevas filas se acumulen pasado tu umbral hacia adelante — no encoge una tabla que ya está hinchada. En Postgres, las filas eliminadas se convierten en tuplas muertas, y el tamaño en disco de execution_data (la tabla de carga útil — la grande, no execution_entity) más sus índices permanece grande hasta que se ejecute VACUUM, y autovacuum a menudo no puede alcanzar una tabla que ya se hizo enorme. Así que “habilitamos la poda y nada cambió” es exactamente lo que esperarías si tu lentitud es E/S de BD. Verifica directamente:
SELECT relname, n_live_tup, n_dead_tup, last_autovacuum FROM pg_stat_user_tables ORDER BY n_dead_tup DESC; — si execution_data tiene millones de tuplas muertas y last_autovacuum es antiguo o nulo, esa es tu respuesta.
SELECT pg_size_pretty(pg_total_relation_size('execution_data')); — si el tamaño físico es enorme, la poda hacia adelante no ayudará; necesitas un pg_repack (se ejecuta en línea, sin bloqueo largo) para recuperarlo realmente. VACUUM FULL también funciona pero bloquea la tabla.
Tu mensaje de error es una señal específica, no lentitud general. “Esta ejecución falló al procesarse demasiadas veces” es la ruta de trabajo estancado: un worker arrendó la ejecución, no terminó ni hizo heartbeat antes de que expirara el bloqueo, se reencoló, y después de N intentos se marca como fallido. Tu bump de bloqueo solo ayuda si la causa son genuinamente ejecuciones largas. La que atrapa a la gente en Railway y se oculta detrás de “CPU saludable” es un worker que alcanza su límite de memoria — Railway reinicia silenciosamente un contenedor que excede su techo de RAM, y cada ejecución en vuelo en ese worker lanza exactamente este error. El porcentaje de CPU se ve bien porque la terminación es memoria, no CPU. Mira el contador de reinicios del servicio worker y la gráfica de memoria (no CPU) y verifica si los reinicios se alinean con las fallas.
Uno más, si algún workflow mueve archivos. Con 40k/mes, si manejas PDFs/imágenes y el modo de datos binarios sigue siendo el predeterminado, ese es un origen de hinchazón de memoria + BD, y también es poco confiable entre múltiples workers (el archivo termina en el disco local de un worker). N8N_DEFAULT_BINARY_DATA_MODE=s3 si es así — ignora si todo es JSON.
El orden en que iría: las dos consultas Postgres primero (descarta BD en aproximadamente un minuto), luego la gráfica de reinicio/memoria del worker (descarta OOM), luego cambia una cosa y observa un número para que la siguiente ronda sea realmente medible.
Si es útil: precisar cuál de estos es realmente tu cuello de botella a partir de tu salida real de pg_stat y métricas de worker es el tipo de análisis que hago como diagnóstico escrito fijo de $49 — envías los resultados de consultas desinfectados + la gráfica de memoria del worker, yo envío la causa raíz y una lista de correcciones priorizada, asincrónico, sin llamada. Si luego quieres que se supervise continuamente — alertas sobre reinicios OOM de worker y espera en cola antes de que se conviertan en ejecuciones fallidas — eso es una configuración de monitoreo de $149/mes. Pero ejecuta esas dos consultas Postgres primero; si es solo hinchazón acumulada, un pg_repack lo arregla y no me necesitarás.
Un seguimiento con un punto que debería haber incluido la primera vez, porque es lo que hace que el consejo de poda parezca que no funcionó.
Si ejecutaste las dos consultas pg_stat y execution_data sigue siendo enorme, verifica si las filas realmente desaparecieron o solo están marcadas como eliminadas antes de concluir que la poda está rota. La poda de n8n realiza la eliminación en etapas, por lo que hay una ventana donde las ejecuciones están marcadas para eliminación pero las filas de carga útil aún están presentes físicamente, y en una instancia ocupada que ha estado reiniciándose, el registro pendiente marcado puede quedarse indefinidamente. Comparar el conteo de filas en execution_entity con lo que la interfaz de usuario te muestra como ejecuciones existentes suele ser suficiente para saber en qué situación estás, y cambia completamente la solución: si las filas ya desaparecieron, tienes inflación de tuplas muertas y necesitas un reempaque, y si todavía están allí, ninguna cantidad de vacío ayudará hasta que se eliminen realmente.
La segunda mitad es importante específicamente en Railway. pg_repack reconstruye la tabla junto a la original antes de intercambiar, por lo que necesita espacio libre aproximadamente igual al tamaño de la tabla y sus índices. Si execution_data es la mayor parte del volumen, el reempaque se ejecutará por un tiempo y luego fallará en disco, y estarás en una posición peor que cuando comenzaste. Verifica el espacio libre contra pg_total_relation_size primero. Si el margen no está ahí, la ruta más económica es reducir el conteo de filas drásticamente primero, en lotes para que no mantengas una transacción enorme, y solo entonces reclama el espacio.
Vale la pena decir claramente que si las dos consultas apuntaron al worker en lugar de la base de datos, nada de lo anterior es tu problema e ignoralo.