SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-26

Почему n8n постоянно отключается на VPS

Разберитесь в причинах сбоев n8n. Статья поможет отличить ошибку WebSocket, циклическую перезагрузку контейнера, нехватку RAM и зависание планировщика задач по логам.

Почему n8n постоянно отключается: четыре сбоя, один симптом

Фраза «n8n постоянно отключается» объединяет четыре различных сбоя, и для каждого из них требуется свое решение. Редактор показывает баннер о потере соединения, хотя контейнер работает в штатном режиме. Контейнер перезапускается самостоятельно. Ядро системы завершает процесс Node.js из-за чрезмерного потребления памяти. Либо с процессом всё в порядке, но активный рабочий процесс (workflow) просто не запускается. Измените не тот параметр, и вы потратите выходные на решение проблемы, которой у вас не было.

Поэтому выясните, с каким именно сбоем вы столкнулись, прежде чем менять конфигурацию. n8n работает как единый процесс Node.js, обычно внутри одного Docker контейнера, за reverse proxy, который выполняет TLS termination (завершение TLS). Каждый из этих уровней может выйти из строя по-своему, но браузер сообщает обо всех этих случаях одним и тем же сообщением.

Диагностика в указанном порядке

Выполните эти команды на 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 при передаче запроса выше или удаляет заголовки upgrade, из-за чего обновление соединения не происходит и редактор постоянно пытается переподключиться. Либо обновление проходит успешно, но позже прокси закрывает сокет из-за отсутствия активности, так как 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. Если остальная часть блока server ниже вам незнакома, построчный разбор блока server в nginx объясняет назначение каждой директивы.

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: 3600s

Caddy обрабатывает обновление автоматически в 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

Использование именованного тома полностью решает эту проблему, так как Docker автоматически назначает ему корректного владельца. Если вам необходим bind mount, выполните chown для хостовой директории, указав числовой идентификатор пользователя, который был выведен первой командой. Важно один раз разобраться в механизме сопоставления прав между хостом и контейнером; в руководстве по 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=false

EXECUTIONS_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 задает максимальный размер входящего webhook-запроса в MiB (мебибайтах), значение по умолчанию — 16. Увеличение этого параметра позволяет принимать более крупные запросы, что является осознанным расходом оперативной памяти.

Любые другие процессы на сервере конкурируют за ту же оперативную память. Если ошибки OOM (Out of Memory) начались после добавления контейнера с базой данных, запуск базы данных в Docker или на хосте — это компромисс, на который вы идете в данной ситуации.

Политика перезапуска и восстановление после перезагрузки

Контейнер без политики перезапуска остается выключенным после завершения работы, а также после перезагрузки хоста. restart: unless-stopped возвращает его в рабочее состояние в обоих случаях, при этом учитывая контейнеры, которые вы остановили вручную. restart: always также перезапускает контейнер, который вы остановили намеренно, как только Docker запустится снова.

n8n предоставляет эндпоинт для проверки работоспособности, задаваемый параметром 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 должен работать совместно с политикой перезапуска или внешним наблюдателем. В написании healthcheck, который действительно выполняет действия и обеспечении автоматического запуска стека после перезагрузки рассматриваются обе эти составляющие.

Рабочий процесс не запускается, хотя n8n работает исправно

В этом случае не выводится баннер и не происходит перезапуск. Контейнер запущен, редактор работает, но ожидаемый запуск отсутствует в списке выполнений. Чаще всего это происходит по четырем причинам.

  • Рабочий процесс не активен. Триггер Schedule работает только в продуктивном режиме, поэтому при тестировании в редакторе расписание не срабатывает.
  • Часовой пояс не совпадает с вашим. GENERIC_TIMEZONE по умолчанию использует America/New_York, поэтому расписание, установленное на 09:00, сработает в 09:00 именно в этой зоне, пока вы не настроите GENERIC_TIMEZONE и TZ на ваш часовой пояс.
  • Пропущенные запуски не выполняются задним числом. Триггеры регистрируются при запуске n8n, поэтому расписание, время которого наступило во время перезагрузки контейнера, не будет выполнено с опозданием. Следующий запуск произойдет в ближайшее время по расписанию после старта.
  • Рабочий процесс был деактивирован автоматически. N8N_WORKFLOW_AUTODEACTIVATION_ENABLED по умолчанию выключен, но если он включен, рабочий процесс, который постоянно завершается с ошибкой, будет снят с публикации. После этого он выглядит точно так же, как процесс, который никто никогда не активировал.

Откройте список выполнений и отфильтруйте его по нужному рабочему процессу. Запись о неудачном выполнении указывает на проблему внутри самого процесса. Если выполнение завершилось с ошибкой 429 при обращении к другому сервису на этом же сервере, ограничение относится к этому сервису, а не к n8n. В руководстве по ошибке 429 в SearXNG показано, как отличить работу собственного ограничителя частоты запросов от блокировки вашего IP-адреса внешними системами. Отсутствие записи в списке означает проблему с триггером, и искать причину следует среди четырех пунктов, описанных выше.

Что изменить в первую очередь

  1. Ознакомьтесь с STATUS, RestartCount и OOMKilled для вашего контейнера, прежде чем редактировать какие-либо файлы.
  2. Если контейнер не завершал работу, исправьте заголовки обновления прокси (proxy upgrade headers) и тайм-аут простоя (idle timeout).
  3. Если OOMKilled имеет значение true, установите выбранное вами ограничение для контейнера, задайте верхний предел кучи Node ниже этого значения и переключите бинарные данные на filesystem.
  4. Если ничего из перечисленного не помогло, проверьте, активен ли рабочий процесс (workflow) и совпадает ли часовой пояс экземпляра с вашим.

Большая часть этих настроек выполняется один раз после успешной установки и в дальнейшем не требует изменений. Если вы все еще находитесь на этапе установки, руководство по развертыванию 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, сопровождающееся ошибкой кучи и трассировкой стека в конце 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 регистрирует триггеры при запуске процесса и не выполняет пропущенные задачи, срок которых наступил во время простоя. Таким образом, цикл перезапусков не приводит к выполнению накопленных задач, а следующее выполнение произойдет в запланированное время после старта. Если вам критически важно выполнение всех задач, инициируйте workflow извне через webhook, чтобы логика повторных попыток находилась за пределами n8n.

Перезапустит ли healthcheck сервис n8n, если он перестанет отвечать?

Сам по себе — нет. Healthcheck в Compose лишь помечает контейнер как работоспособный или неработоспособный. За перезапуск отвечает политика перезапуска, поэтому restart: unless-stopped возвращает контейнер в работу после завершения процесса, а также после перезагрузки хоста, если служба Docker включена. Проверьте это с помощью sudo systemctl is-enabled docker. Для автоматического реагирования на состояние unhealthy требуется внешний наблюдатель, который отслеживает статус и перезапускает сервис.