SSD Nodes Learn Hosting plans →
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-28

Чому n8n переходить в офлайн на VPS

Чотири несправності дають один симптом: банер websocket, цикл перезапусків, завершення Node.js через нестачу пам’яті або несправний розклад. Дізнайтеся, як їх розрізнити.

Чому n8n переходить в офлайн-режим: чотири несправності, один симптом

Фраза «n8n переходить в офлайн-режим» може означати одну з чотирьох різних несправностей, і для кожної потрібне окреме виправлення. Редактор показує повідомлення про втрату з’єднання, хоча контейнер працює нормально. Контейнер самостійно перезапускається. Ядро завершує процес Node.js через надмірне споживання пам’яті. Або з процесом усе гаразд, але активний workflow просто не запускається. Якщо змінити неправильний параметр, можна витратити вихідні на проблему, якої насправді не існувало.

Тому спочатку визначте, яка саме несправність виникла, і лише потім змінюйте конфігурацію. n8n працює як один процес Node.js, зазвичай в одному Docker-контейнері, за reverse proxy, який завершує TLS (transport layer security). Кожен із цих рівнів може вийти з ладу по-своєму, але браузер повідомляє про всі ці проблеми однаковим повідомленням.

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

Виконайте ці команди на VPS (virtual private server) і перевірте значення, які виводить саме ваш сервер. Не порівнюйте їх із числами з допису на форумі. Тут важливі значення описують ваш сервер, а не чужий.

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 показує поточне використання пам’яті поруч із чинним обмеженням. Залиште цю команду запущеною в іншому терміналі, запустіть workflow, під час якого виникає проблема, і спостерігайте, як змінюється значення під час збою.


Банер про втрату з’єднання зазвичай вказує на проблему в reverse proxy

Редактор n8n підтримує одне довготривале push-з’єднання з бекендом, щоб передавати перебіг виконання на canvas. Типово це WebSocket; саме його вибирає N8N_PUSH_BACKEND, а значення за замовчуванням — websocket. WebSocket починається як звичайний HTTP-запит із заголовками Connection: Upgrade і Upgrade: websocket. Сервер відповідає 101 Switching Protocols, після чого обидві сторони використовують той самий TCP-сокет для двонапрямленого обміну даними.

Це з’єднання може перерватися з двох причин, і обидві пов’язані з proxy, а не з n8n. Proxy використовує HTTP/1.0 для підключення до upstream або видаляє заголовки upgrade. Через це upgrade не відбувається, а редактор безперервно підключається повторно. Або upgrade успішно завершується, але згодом proxy закриває сокет через відсутність трафіку. WebSocket без повідомлень виглядає як неактивне з’єднання. В обох випадках контейнер працює справно. Банер означає, що браузер втратив канал зв’язку.

Перш ніж щось змінювати, перевірте це в браузері. Відкрийте інструменти розробника, перейдіть на вкладку Network, виберіть фільтр WS і перезавантажте редактор. Push-запит має досягти 101 Switching Protocols і залишатися відкритим. Якщо push-запит повертає звичайний код стану або з’являється знову кожні кілька секунд, проблема, найімовірніше, у proxy.

Параметри nginx, які підтримують підключення редактора

nginx не пересилає upgrade-запит, якщо явно не налаштувати це. proxy_pass за замовчуванням використовує HTTP/1.0 для підключення до бекенду, а Connection і Upgrade — це hop-by-hop заголовки, які nginx видаляє під час пересилання. Потрібно додати обидва заголовки знову. Блок map розміщують у контексті http, а не всередині server. Якщо решта server block нижче незрозуміла, покроковий розбір server block 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 після upgrade. Тому вкладка редактора, залишена відкритою на неактивному екземплярі, втрачає підключення приблизно через хвилину після проходження останнього повідомлення. Збільшення цього значення усуває банер, який ви бачите після повернення до залишеної відкритою вкладки.

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

nginx -T виводить усю активну конфігурацію, а не один файл, тому підтверджує, що зміни справді завантажено. Якщо конфігурація міститься у файлі, який не підключає жоден рядок include, правильне виправлення не матиме ефекту.

Після цього повідомте n8n, що він працює за проксі, оскільки 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. Встановіть значення, що відповідає кількості проксі перед контейнером. Станом на August 2026 поточна назва — N8N_WEBHOOK_URL, а старий параметр WEBHOOK_URL ще працює, але під час запуску виводить попередження про застарілу функцію.

Traefik передає WebSocket upgrade, а потім завершує з’єднання через тайм-аут

Traefik передає WebSocket upgrade без middleware і додаткових labels. Тому користувач Traefik, який бачить цей банер, зазвичай стикається з тайм-аутом, а не з відсутнім заголовком. Параметри налаштовуються в entryPoint. Станом на August 2026 у Traefik v3 параметр idleTimeout за замовчуванням має значення 180 seconds, а readTimeout — 60 seconds.

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

Caddy автоматично обробляє upgrade у reverse_proxy і не потребує для цього окремої директиви. Якщо ви взагалі не можете змінити proxy, оскільки ним керує інша сторона, перемкніть push channel за допомогою N8N_PUSH_BACKEND=sse. SSE (server-sent events) — це звичайна HTTP-відповідь, яку залишають відкритою. Тому такий підхід працює через proxy, який відхиляє upgrade, хоча надто короткий idle timeout усе одно перерве з’єднання. Вибір самого proxy — окреме рішення. Порівняння nginx, Caddy і Traefik пояснює, яких операційних витрат потребує кожен варіант.

Коли контейнер справді перезапускається

Якщо RestartCount зростає, контейнер завершує роботу, а Docker запускає його знову. Зіставте часові позначки в журналі з кожним перезапуском і прочитайте повідомлення безпосередньо перед ним. Майже всі випадки пояснюються чотирма причинами: помилка конфігурації, яка зупиняє запуск, база даних, до якої n8n не може підключитися, збій уже запущеного процесу або завершення через нестачу пам’яті.

Почніть із тому, оскільки проблеми з правами доступу часто залишаються непомітними. Офіційний image запускається від непривілейованого користувача 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

Іменований volume повністю усуває цю проблему, оскільки Docker створює його з правильним власником. Якщо потрібен bind mount, chown каталог на хості на числовий ідентифікатор користувача, який вивела перша команда. Відповідність власників між хостом і контейнером варто зрозуміти один раз. У поясненні PUID і PGID описано, як ці images визначають користувача, від імені якого записують файли.

Вимкнення процесу через нестачу пам’яті, яке виглядає як аварійне завершення

Для процесу n8n діють два окремі обмеження пам’яті, і порушення кожного має різні наслідки. Ліміт control group контейнера застосовує kernel: якщо його перевищити, процес негайно завершується без можливості щось записати, а OOMKilled повертає true. Ліміт heap V8 застосовується всередині Node.js: якщо його перевищити, Node створює помилку heap зі stack trace і завершується самостійно, тому OOMKilled повертає false. У браузері ці випадки виглядають однаково. У docker inspect між ними є лише одне поле.

Встановіть ліміт heap Node нижче за ліміт контейнера. Якщо ліміт heap вищий, V8 продовжує виділяти пам’ять після точки, у якій kernel завершує процес. Тому його garbage collector не досягає власного ліміту, і ви завжди отримуєте жорсткіший варіант завершення без журналу для аналізу.

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 зберігає ці дані. Це має два наслідки. Пікове споживання пам’яті одного запуску визначається найбільшим пакетом даних, який через нього проходить. Тому workflow, що обробляє десять тисяч рядків за раз, є іншою програмою, ніж той самий workflow, який обробляє по двісті рядків. Збережена копія також продовжує зростати, доки її не буде видалено.

Очищення вирішує другу проблему. Станом на August 2026 за замовчуванням очищення увімкнене, EXECUTIONS_DATA_MAX_AGE становить 336 hours (14 days), а 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 — агресивне налаштування. Воно зберігає невдалі виконання для налагодження та видаляє успішні. Приймайте це рішення свідомо: якщо workflow сформував неправильний результат без помилки, вам не залишиться даних для перевірки. Очищення також спочатку позначає рядки як видалені, а видаляє їх під час наступного проходу. SQLite повторно використовує звільнені сторінки, а не повертає їх операційній системі, тому розмір файлу на диску не зменшиться одразу після зміни налаштування.

Щоб зменшити пікове споживання пам’яті, а не лише обсяг збережених даних, передавайте менше даних за один запуск. Розділяйте великі завдання на sub-workflows, які повертають батьківському workflow невеликі результати, обробляйте дані пакетами за допомогою вузла Loop Over Items і не завантажуйте цілі набори даних у вузол Code.

Бінарні файли не повинні зберігатися в пам’яті

N8N_DEFAULT_BINARY_DATA_MODE за замовчуванням має значення default, через що бінарні дані зберігаються в пам’яті запущеного виконання. Кожен файл, який завантажує node, і кожна копія, передана наступному node, залишаються там до завершення виконання. Один workflow, який отримує кілька великих вкладень, може перевищити ліміт, до якого звичайна робота з JSON ніколи не наближається. Тому збій виникає під час одного конкретного workflow, а не через певний час роботи.

environment:
  - N8N_DEFAULT_BINARY_DATA_MODE=filesystem

За допомогою filesystem бінарні дані записуються в N8N_BINARY_DATA_STORAGE_PATH. За замовчуванням цей каталог розташований у папці користувача n8n, тому дані потрапляють на той самий том, що й решта файлів. Перед перемиканням перевірте, чи достатньо вільного місця на томі. N8N_PAYLOAD_SIZE_MAX визначає максимальний розмір вхідного payload webhook у MiB (мебібайтах); значення за замовчуванням — 16. Збільшення цього значення дає змогу приймати більші запити, але збільшує споживання пам’яті.

Усі інші процеси та сервіси на сервері також використовують ту саму RAM. Якщо OOM kills почалися після додавання database container, запуск database у 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 нічого не перезапускає. Compose позначає контейнер як несправний і на цьому зупиняється. Тому healthcheck потребує політики перезапуску або зовнішнього watcher-процесу, щоб мати практичний ефект. Написання healthcheck, який справді діє і повторний запуск stack після перезавантаження охоплюють обидві частини.

Процес, який ніколи не запускається, хоча n8n працює

Цей процес не виводить банер і не перезапускається. Контейнер працює, редактор відкривається, але очікуваний запуск відсутній у списку виконань. У більшості випадків причина одна з чотирьох.

  • Процес неактивний. Schedule Trigger запускається лише у production-середовищі, тому його тестування на canvas не створює розклад.
  • Вибрано не ваш часовий пояс. GENERIC_TIMEZONE за замовчуванням використовує America/New_York, тому розклад на 09:00 спрацьовує о 09:00 у цьому часовому поясі, доки ви не встановите GENERIC_TIMEZONE і TZ для свого часового поясу.
  • Пропущені запуски не виконуються пізніше. Тригери реєструються під час запуску n8n, тому розклад, час якого настав під час перезапуску контейнера, не запускається із запізненням. Наступний запуск відбудеться у найближчий запланований час після запуску.
  • Процес було деактивовано автоматично. N8N_WORKFLOW_AUTODEACTIVATION_ENABLED за замовчуванням вимкнено. Якщо його ввімкнено, процес, який постійно завершується з помилкою, скасовує публікацію. Після цього він виглядає так, ніби його ніколи не активували.

Відкрийте список виконань і відфільтруйте його за цим процесом. Наявність невдалого виконання означає, що проблема пов’язана з процесом. Якщо воно завершилося з помилкою 429 під час звернення до іншого сервісу, розгорнутого на тому самому сервері, обмеження встановлює цей сервіс, а не n8n. У інструкції щодо помилки 429 у SearXNG показано, як відрізнити його власний rate limiter від пошукових рушіїв, які блокують IP-адресу вашого сервера. Якщо записів немає зовсім, проблема пов’язана з тригером. Перевірте чотири наведені вище причини.

Що змінити спочатку

  1. Самостійно перегляньте STATUS, RestartCount і OOMKilled у власному контейнері, перш ніж редагувати будь-який файл.
  2. Якщо контейнер не зупинявся, виправте заголовки для оновлення proxy-з’єднання та тайм-аут бездіяльності.
  3. Якщо OOMKilled має значення true, навмисно встановіть ліміт контейнера, задайте граничний обсяг heap для 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, а не файл, який ви редагували.

Як відрізнити завершення процесу через нестачу пам’яті від звичайного аварійного завершення?

Виконайте docker inspect n8n | grep -iE 'OOMKilled|ExitCode|RestartCount' і перевірте прапорець OOMKilled. Значення True означає, що kernel завершив процес через перевищення ліміту пам’яті. У журналі контейнера в такому разі не буде корисної інформації, оскільки процес не встиг записати її. Значення False разом із помилкою heap і stack trace наприкінці 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 реєструє тригери під час запуску процесу. Розклади, час яких настав під час його роботи, n8n не відтворює. Тому цикл перезапусків призводить до відсутності запусків, а не до серії пропущених запусків після відновлення. Наступне виконання відбудеться в найближчий запланований час після запуску. Якщо пропуски неприпустимі, запускайте workflow із зовнішнього клієнта через webhook. Тоді логіка повторних спроб буде розташована поза n8n.

Чи перезапустить healthcheck n8n, якщо сервіс перестане відповідати?

Ні, не самостійно. healthcheck у Compose лише позначає контейнер як healthy або unhealthy. За перезапуск відповідає restart policy. Тому restart: unless-stopped повертає контейнер після завершення його роботи, а також запускає його після перезавантаження хоста, якщо сервіс Docker увімкнено. Перевірте це за допомогою sudo systemctl is-enabled docker. Щоб реагувати саме на стан unhealthy, потрібен зовнішній watcher, який читає статус і перезапускає сервіс.