Почему n8n постоянно отключается на VPS
Разберитесь в причинах сбоя n8n. Статья поможет отличить ошибку WebSocket, циклическую перезагрузку контейнера, OOM Kill процесса Node.js и зависание планировщика задач.
Почему n8n постоянно отключается: четыре сбоя, один симптом
Фраза «n8n постоянно отключается» объединяет четыре различных сбоя, каждый из которых требует своего решения. Редактор показывает баннер о потере соединения, хотя контейнер работает в штатном режиме. Контейнер перезапускается самостоятельно. Ядро системы завершает процесс Node.js из-за чрезмерного потребления памяти. Либо с самим процессом всё в порядке, но активный рабочий процесс (workflow) просто не запускается. Измените не ту настройку — и вы потратите выходные на решение проблемы, которой у вас не было.
Поэтому выясните, с каким именно сбоем вы столкнулись, прежде чем менять конфигурацию. n8n работает как единый процесс Node.js, обычно внутри одного Docker контейнера, за reverse proxy, который выполняет TLS termination (завершение шифрования). Каждый из этих уровней может выйти из строя по-своему, но браузер сообщает обо всех этих случаях одним и тем же сообщением.
Диагностика в указанном порядке
Выполните эти команды на VPS (виртуальном выделенном сервере) и изучите значения, которые выведет ваша система. Не сравнивайте их с цифрами из веток форумов. Важны только те значения, которые описывают ваш сервер, а не чужой.
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Столбец STATUS из вывода docker ps -a показывает, как долго контейнер находится в текущем состоянии. Сравните это время с моментом возникновения проблемы. Если контейнер работает дольше, чем отображается баннер, значит, n8n не отключался. Проблема заключается в соединении между вашим браузером и бэкендом, а именно в пути websocket, который рассматривается в следующем разделе.
RestartCount — это количество перезапусков контейнера со стороны Docker. Запишите это число, подождите минуту и проверьте снова. Если число растет на ваших глазах, значит, возник цикл перезапусков, а строки лога непосредственно перед каждым перезапуском содержат причину сбоя.
OOMKilled — это логический флаг (true или false). Значение true означает, что ядро Linux принудительно завершило процесс из-за превышения лимита памяти — либо лимита самого контейнера, либо лимита всей системы. Этот единственный параметр позволяет отличить принудительное завершение по нехватке памяти от любого другого типа выхода, поэтому его нужно проверять до того, как делать предположения.
ExitCode — это код, с которым контейнер завершился в последний раз. Вам не нужно запоминать значение каждого кода. Прочитайте свой код, а затем изучите конец вывода docker logs с той же временной меткой. Только совокупность последних строк лога и флага нехватки памяти дает верную картину; по отдельности они могут ввести в заблуждение.
docker stats показывает текущее потребление памяти в сравнении с действующим лимитом. Запустите эту команду во втором терминале, выполните рабочий процесс, который вызывает сбой, и наблюдайте за изменением числа в момент возникновения ошибки.
Баннер о потере соединения обычно указывает на проблемы с reverse proxy
Редактор n8n поддерживает одно долгоживущее push-соединение с бэкендом для потоковой передачи прогресса выполнения на рабочую область. По умолчанию это соединение является WebSocket, что определяется параметром N8N_PUSH_BACKEND, значение которого по умолчанию — websocket. WebSocket начинается как обычный HTTP-запрос, содержащий заголовки Connection: Upgrade и Upgrade: websocket. Сервер отвечает 101 Switching Protocols, после чего обе стороны используют один и тот же TCP-сокет для двустороннего обмена данными.
Две причины могут нарушить этот процесс, и обе они связаны с прокси, а не с n8n. Либо прокси использует HTTP/1.0 при передаче запроса выше (upstream) или удаляет заголовки обновления (upgrade headers), из-за чего обновление не происходит и редактор пытается переподключиться бесконечно. Либо обновление проходит успешно, но позже прокси закрывает сокет из-за отсутствия активности, так как WebSocket без сообщений выглядит как простаивающее соединение. В обоих случаях контейнер работает исправно. Баннер означает, что браузер сообщает о потере канала связи.
Подтвердите это в браузере перед внесением любых изменений. Откройте инструменты разработчика, перейдите на вкладку Network, отфильтруйте запросы по WS и перезагрузите редактор. Push-запрос должен получить статус 101 Switching Protocols и оставаться открытым. Если push-запрос возвращает обычный код состояния или появляется снова каждые несколько секунд, проблема заключается в прокси.
Настройки nginx для поддержания соединения с редактором
nginx не пересылает запрос на обновление (upgrade), если вы его об этом не попросите. По умолчанию proxy_pass использует протокол HTTP/1.0 при общении с бэкендом, а Connection и Upgrade являются заголовками hop-by-hop, которые nginx удаляет при передаче. Вам необходимо вернуть их обратно. Блок map следует размещать в контексте http, а не внутри 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 — это та строка, которую часто забывают добавить. Её значение по умолчанию составляет 60 секунд, и оно применяется даже к обновленному соединению WebSocket. В результате вкладка редактора, оставленная открытой на неактивном экземпляре, теряет соединение примерно через минуту после последнего сообщения. Увеличение этого значения устраняет появление баннера при возвращении к открытой вкладке.
sudo nginx -t && sudo systemctl reload nginx
sudo nginx -T | grep -iE 'proxy_http_version|upgrade|proxy_read_timeout'nginx -T выводит всю текущую конфигурацию, а не содержимое одного файла, поэтому команда позволяет убедиться, что ваши изменения действительно загружены. Если конфигурация находится в файле, который не подключен через include, то даже верное исправление не даст никакого эффекта.
Затем укажите n8n, что сервис находится за прокси, так как приложение формирует URL-адреса на основе этих значений.
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 по умолчанию равно 0, что означает, что n8n воспринимает адрес подключения как адрес клиента и игнорирует X-Forwarded-For. Установите его равным количеству прокси-серверов перед контейнером. По состоянию на август 2026 года N8N_WEBHOOK_URL является актуальным параметром, а старый WEBHOOK_URL всё ещё работает, но при запуске выводит предупреждение об устаревании.
Traefik пересылает WebSocket, но завершает их по таймауту
Traefik пересылает запрос на обновление (upgrade) WebSocket без использования middleware и дополнительных меток. Если пользователь Traefik видит подобное сообщение, это обычно означает срабатывание таймаута, а не отсутствие заголовка. Настройки находятся в entryPoint. По состоянию на август 2026 года в Traefik v3 значение idleTimeout по умолчанию составляет 180 секунд, а readTimeout — 60 секунд.
entryPoints:
websecure:
address: ":443"
transport:
respondingTimeouts:
readTimeout: 0
idleTimeout: 3600sCaddy обрабатывает обновление автоматически в reverse_proxy и не требует для этого специальных директив. Если вы не можете изменить прокси, так как он находится в ведении другого администратора, переключите канал передачи данных на N8N_PUSH_BACKEND=sse. SSE (server-sent events) — это обычный HTTP-ответ, который поддерживается открытым, поэтому он проходит через прокси, блокирующие запросы на обновление, хотя агрессивный таймаут простоя всё равно может его прервать. Выбор самого прокси — это отдельное решение, и в сравнении nginx, Caddy и Traefik рассматриваются эксплуатационные издержки каждого из них.
Когда контейнер действительно постоянно перезапускается
Если RestartCount растёт, значит, контейнер завершается с ошибкой, и Docker перезапускает его. Сопоставьте временные метки в логах с моментами перезапуска и изучите записи, которые предшествовали сбою. Почти все проблемы вызваны одной из четырёх причин: ошибка конфигурации, препятствующая запуску; недоступная для n8n база данных; аварийное завершение в процессе работы; или принудительное завершение из-за нехватки памяти (OOM kill).
Начните с тома, так как проблемы с правами доступа часто остаются незамеченными. Официальный образ работает от имени непривилегированного пользователя node и хранит данные в /home/node/.n8n. Если bind mount создан пользователем root, у этого пользователя нет прав на запись, поэтому процесс каждый раз завершается при запуске, а политика перезапуска скрывает это в бесконечном цикле.
docker compose config
docker run --rm -it --entrypoint sh docker.n8n.io/n8nio/n8n -c 'id'
docker exec n8n ls -ld /home/node/.n8nИспользование именованного тома (named volume) полностью решает эту проблему, так как Docker создаёт его с корректными правами владения. Если вам необходим bind mount, выполните chown для хостовой директории, указав числовой идентификатор пользователя (UID), который был выведен первой командой. Важно один раз разобраться в механизме сопоставления прав между хостом и контейнером; в руководстве по PUID и PGID подробно описано, как эти образы определяют владельца файлов.
Завершение процесса по нехватке памяти, выглядящее как сбой
Существует два отдельных ограничения по памяти для процесса n8n, и они приводят к разным результатам при превышении. Лимит контрольной группы (cgroup) контейнера принудительно устанавливается ядром: при его достижении процесс немедленно завершается без возможности что-либо записать, а OOMKilled принимает значение true. Лимит кучи V8 устанавливается внутри Node.js: при его превышении Node выдает ошибку кучи со стеком вызовов и завершается самостоятельно, поэтому OOMKilled принимает значение false. В браузере эти события выглядят одинаково. В docker inspect их разделяет всего одно поле.
Установите лимит кучи Node ниже лимита контейнера. Если лимит кучи выше, V8 продолжает выделять память даже после того, как в процесс вмешивается ядро. В результате сборщик мусора никогда не достигает собственного предела, и вы всегда получаете более жесткий вариант завершения без возможности прочитать логи.
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>Выберите оба значения исходя из реальных ресурсов вашего VPS, оставляя запас для базы данных, прокси-сервера и операционной системы. docker stats --no-stream выводит текущее использование рядом с действующим лимитом, поэтому вы можете проверить, что установленный вами лимит был применен Docker. В Как применяются лимиты памяти в Compose подробно описано, какой параметр имеет приоритет при установке нескольких ограничений.
Данные выполнения накапливаются в фоновом режиме
Единичное выполнение сохраняет вывод каждого узла во время работы процесса, после чего n8n записывает эти данные. Из этого следуют два вывода. Пиковое потребление памяти при одном запуске определяется самым большим пакетом данных, который вы через него пропускаете, поэтому рабочий процесс, обрабатывающий десять тысяч строк за раз, — это не то же самое, что тот же процесс, обрабатывающий по двести строк. Кроме того, сохраненная копия продолжает расти, пока её что-то не удалит.
Очистка (pruning) решает вторую проблему. По состоянию на август 2026 года по умолчанию очистка включена, EXECUTIONS_DATA_MAX_AGE установлено на 336 часов (14 дней), а EXECUTIONS_DATA_PRUNE_MAX_COUNT — на 10000. Это достаточно щедрые значения для небольшого VPS на SQLite, где один файл хранит всё, а процесс, обслуживающий редактор, одновременно занимается чтением и записью.
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=falseEXECUTIONS_DATA_SAVE_ON_SUCCESS=none — это агрессивная настройка. Она сохраняет неудачные выполнения для отладки и удаляет успешные. Принимайте это решение осознанно, так как если рабочий процесс выдаст неверный результат без возникновения ошибки, у вас не останется данных для анализа. Очистка сначала помечает строки как удаленные, а затем удаляет их при последующем проходе. Поскольку SQLite повторно использует освобожденные страницы, а не возвращает их системе, размер файла на диске не уменьшится сразу после изменения настройки.
Чтобы снизить пиковую нагрузку, а не общий объем хранимых данных, передавайте меньше данных за один запуск. Разбивайте крупные задачи на подпроцессы, которые возвращают родительскому процессу небольшие результаты, используйте пакетную обработку через узел Loop Over Items и не храните целые наборы данных внутри узла Code.
Бинарные файлы не должны передаваться через оперативную память
N8N_DEFAULT_BINARY_DATA_MODE по умолчанию использует default, что сохраняет бинарные данные в оперативной памяти запущенного процесса. Каждый файл, который загружает узел, и каждая копия, передаваемая следующему узлу, остаются там до завершения выполнения. Один рабочий процесс, получающий несколько крупных вложений, может привести к превышению лимита памяти, к которому обычные JSON-задачи даже не приближаются. Именно поэтому сбои происходят при выполнении конкретного рабочего процесса, а не по расписанию.
environment:
- N8N_DEFAULT_BINARY_DATA_MODE=filesystemПри использовании filesystem бинарные данные записываются в N8N_BINARY_DATA_STORAGE_PATH, который по умолчанию находится внутри пользовательской папки n8n и, следовательно, размещается на том же томе, что и всё остальное. Перед переключением убедитесь, что на томе достаточно свободного места. N8N_PAYLOAD_SIZE_MAX задает максимальный размер входящего полезного содержимого вебхука в MiB (мебибайтах), значение по умолчанию — 16. Увеличение этого параметра позволяет принимать более крупные запросы, что влечет за собой соответствующие затраты оперативной памяти.
Любые другие процессы на сервере конкурируют за ту же оперативную память. Если ошибки OOM (Out of Memory) начали возникать после добавления контейнера с базой данных, то запуск базы данных в Docker или на хосте — это компромисс, на который вы идете в текущей конфигурации.
Политика перезапуска и восстановление после перезагрузки
Контейнер без политики перезапуска остается выключенным после завершения работы, а также после перезагрузки хоста. Параметр restart: unless-stopped возвращает его в рабочее состояние в обоих случаях, при этом учитывая контейнеры, которые вы остановили вручную. Параметр restart: always также перезапускает контейнер, который был остановлен намеренно, при следующем запуске Docker.
n8n предоставляет эндпоинт для проверки состояния (health endpoint), заданный параметром N8N_ENDPOINT_HEALTH, который по умолчанию имеет значение healthz. Сначала проверьте его с хоста, чтобы убедиться, что путь на вашем экземпляре указан верно.
curl -fsS http://127.0.0.1:5678/healthz
docker exec n8n which wget curl
sudo systemctl is-enabled dockerСама по себе проверка состояния (healthcheck) ничего не перезапускает. Docker Compose помечает контейнер как неработоспособный (unhealthy) и на этом останавливается, поэтому для получения результата проверка состояния должна работать совместно с политикой перезапуска или внешним наблюдателем. В разделах Написание healthcheck, который выполняет действия и Настройка автозапуска стека после перезагрузки рассматриваются обе эти части.
Рабочий процесс не запускается, хотя n8n работает исправно
В этом случае нет ни баннера, ни перезапуска. Контейнер запущен, редактор работает, но ожидаемый запуск отсутствует в списке выполнений. Большинство проблем вызвано четырьмя причинами.
- Рабочий процесс не активирован. Триггер Schedule работает только в продуктивном режиме, поэтому при тестировании на холсте (canvas) расписание не срабатывает.
- Часовой пояс не совпадает с вашим.
GENERIC_TIMEZONEпо умолчанию используетAmerica/New_York, поэтому расписание, установленное на 09:00, сработает в 09:00 именно в этой зоне, пока вы не настроитеGENERIC_TIMEZONEиTZна ваш часовой пояс. - Пропущенные запуски не выполняются задним числом. Триггеры регистрируются при запуске n8n, поэтому расписание, время которого наступило во время перезагрузки контейнера, не будет выполнено с опозданием. Следующий запуск произойдет в ближайшее время по расписанию после старта.
- Рабочий процесс был деактивирован автоматически.
N8N_WORKFLOW_AUTODEACTIVATION_ENABLEDпо умолчанию выключена. Если она включена, рабочий процесс, который постоянно завершается с ошибкой, снимается с публикации, после чего он выглядит так, будто его никто никогда не активировал.
Откройте список выполнений и отфильтруйте его по нужному рабочему процессу. Наличие записи с ошибкой означает проблему в самом рабочем процессе. Отсутствие записей указывает на проблему с триггером, и в этом случае следует проверить четыре причины, указанные выше.
Что изменить в первую очередь
- Ознакомьтесь с
STATUS,RestartCountиOOMKilledдля вашего контейнера, прежде чем редактировать какие-либо файлы. - Если контейнер не завершал работу, исправьте заголовки обновления прокси и тайм-аут простоя.
- Если
OOMKilledимеет значение true, установите выбранный вами лимит контейнера, ограничьте кучу Node ниже этого значения и переключите бинарные данные наfilesystem. - Если ничего не сработало, убедитесь, что рабочий процесс активен, а часовой пояс экземпляра совпадает с вашим.
Большая часть этих настроек выполняется один раз после успешной установки. Если вы всё ещё находитесь на этапе развёртывания, руководство по установке n8n в Docker с HTTPS является базой, для которой предназначены эти параметры.
FAQ
Почему в редакторе n8n отображается уведомление о потере соединения, хотя контейнер запущен?
Редактор поддерживает открытое WebSocket-соединение для передачи данных о ходе выполнения. Если ваш reverse proxy не передает заголовки Connection: Upgrade и Upgrade: websocket или не использует HTTP/1.1 при работе с upstream, процедура upgrade не завершается, и браузер постоянно пытается переподключиться, хотя сам n8n работает корректно. В nginx необходимо добавить proxy_http_version 1.1, обе строки proxy_set_header, а также увеличить proxy_read_timeout сверх стандартных 60 секунд, чтобы неактивная вкладка не закрывалась. Проверяйте текущую конфигурацию через sudo nginx -T, а не файл, который вы редактировали.
Как отличить принудительное завершение из-за нехватки памяти (OOM kill) от обычного сбоя?
Выполните docker inspect n8n | grep -iE 'OOMKilled|ExitCode|RestartCount' и проверьте флаг OOMKilled. Значение True означает, что ядро завершило процесс из-за превышения лимита памяти; в логах контейнера полезной информации не будет, так как процесс не успел ничего записать. Значение False, сопровождаемое ошибкой heap и трассировкой стека в конце docker logs, означает, что Node.js достиг собственного лимита V8 heap и завершился самостоятельно. Установите NODE_OPTIONS=--max-old-space-size ниже лимита контейнера, чтобы получить второй тип ошибки, который оставляет записи в логах.
Освобождается ли место на диске сразу после очистки данных о выполнении?
Нет. EXECUTIONS_DATA_PRUNE помечает старые записи для удаления, а их фактическое удаление происходит позже, согласно расписанию EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL. В случае с SQLite файл базы данных повторно использует освобожденные страницы вместо возврата места файловой системе, поэтому размер файла на диске не уменьшается сразу после удаления строк. Установите EXECUTIONS_DATA_MAX_AGE и EXECUTIONS_DATA_PRUNE_MAX_COUNT в значения, соответствующие вашим ресурсам, и проверьте результат на следующий день, а не сразу.
Почему запланированный workflow не выполнился во время перезапуска n8n?
n8n регистрирует триггеры при запуске процесса и не выполняет пропущенные по расписанию задачи, которые должны были сработать во время простоя. Таким образом, цикл перезапуска не приводит к массовому выполнению накопленных задач, а следующее выполнение произойдет в ближайшее время согласно расписанию после старта. Если вам критически важно выполнение всех задач, используйте внешний вызов через webhook, чтобы логика повторных попыток находилась вне n8n.
Перезапустит ли healthcheck контейнер n8n, если он перестанет отвечать?
Сам по себе — нет. Healthcheck в Compose лишь помечает контейнер как работоспособный или неработоспособный. Перезапуск — это задача политики перезапуска, поэтому restart: unless-stopped возвращает контейнер в работу после завершения процесса, а также после перезагрузки хоста (при условии, что служба Docker включена). Проверьте это с помощью sudo systemctl is-enabled docker. Чтобы реагировать именно на статус unhealthy, вам потребуется внешний наблюдатель вне Docker, который отслеживает статус и перезапускает сервис.