Часові пояси n8n: чому Schedule Trigger спізнюється
n8n читає часовий пояс із трьох рівнів: TZ контейнера, GENERIC_TIMEZONE і workflow. Дізнайтеся, чому Schedule Trigger може запускатися на 6 годин пізніше.
Чому Schedule Trigger у n8n спрацьовує не в ту годину
Schedule Trigger у n8n спрацьовує не в ту годину, тому що n8n читає часовий пояс із трьох окремих місць. Виправлення лише одного з них усуває тільки частину проблеми. Це змінна TZ самого контейнера, значення за замовчуванням для інстансу GENERIC_TIMEZONE і часовий пояс, заданий в окремому workflow. Налаштуйте всі три один раз — і кожен наступний розклад запускатиметься в очікуваний час.
Спочатку виправте поширене припущення. Новий self-hosted інстанс n8n не планує запуск у UTC (координований всесвітній час). Годинник контейнера працює за UTC, оскільки офіційний образ не задає TZ. Планування є окремим рівнем, а задокументоване значення за замовчуванням для GENERIC_TIMEZONE — America/New_York (станом на August 2026). Тому незмінений інстанс запускає Schedule Trigger за часом Нью-Йорка. Через це різниця в часі, про яку повідомляють користувачі, рідко відповідає їхній власній різниці з UTC. Власник у Берліні, який задає 06:00, отримує запуск о 12:00 за місцевим часом, а протягом кількох тижнів у березні, коли Сполучені Штати вже перейшли на літній час, а Європа ще ні, — о 11:00.
Три рівні часових поясів і пріоритет між ними
TZ — це часовий пояс операційної системи всередині контейнера. Документація n8n описує його як змінну, що встановлює системний часовий пояс і визначає результат команд та скриптів, наприклад date. Від нього залежить, що повертає date усередині контейнера, яку часову мітку отримує рядок у журналі контейнера, що повертає new Date() у вузлі Code і який часовий пояс бачить будь-який shell-скрипт, запущений у контейнері. На час запуску Schedule Trigger він не впливає.
GENERIC_TIMEZONE — це часовий пояс екземпляра n8n. Документація називає його часовим поясом екземпляра n8n і зазначає, що він важливий для вузлів розкладу, таких як Cron. Тут Cron означає стандартний синтаксис планування за часом. У n8n він доступний як параметр Custom (Cron) у Schedule Trigger.
Часовий пояс workflow налаштовується окремо для кожного workflow. Відкрийте workflow на canvas, натисніть три крапки у верхньому правому куті, виберіть Settings, а потім змініть значення Timezone. Для цього workflow він має пріоритет над GENERIC_TIMEZONE.
Для Schedule Trigger порядок фіксований. n8n використовує часовий пояс workflow, якщо його задано. Інакше використовується часовий пояс екземпляра зі GENERIC_TIMEZONE. Якщо його також не задано, використовується вбудоване значення America/New_York. TZ на жодному етапі цього вибору не враховується.
Для дат усередині вузлів відповідь залежить від того, який годинник запитує код. Luxon, бібліотека дат, яку використовують вирази n8n, використовує часовий пояс n8n. Тому $now і $today дотримуються того самого порядку «workflow, потім екземпляр», що й тригер. Звичайний JavaScript new Date() у вузлі Code звертається до операційної системи, тому використовує TZ. Саме цей поділ найчастіше спричиняє плутанину: тригер може працювати правильно, а кожна часова мітка, яку записує workflow, відставати або випереджати на кілька годин.
Укажіть усі три параметри у файлі Compose
Розмістіть TZ і GENERIC_TIMEZONE поруч один з одним у файлі, щоб ніхто не вказав один параметр і не забув про інший. Фрагмент нижче містить частину робочого сервісу, що стосується часового поясу. Решта файлу, reverse proxy і сертифікат, описані в матеріалі про self-hosted n8n на VPS за HTTPS.
services:
n8n:
image: docker.n8n.io/n8nio/n8n
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678"
environment:
- GENERIC_TIMEZONE=Europe/Berlin
- TZ=Europe/Berlin
- N8N_RUNNERS_ENABLED=true
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:Застосуйте зміни за допомогою docker compose up -d, а не docker compose restart. Перезапуск запускає той самий контейнер із середовищем, з яким його було створено, тому зміни у файлі не змінюють середовище запущеного процесу. up -d виявляє змінене середовище та повторно створює контейнер. Якщо замість вбудованих значень ви зберігаєте їх у env-файлі, діє те саме правило повторного створення, а в посібнику про env-файл Compose і роботу із секретами описано, звідки читається цей файл.
Використовуйте назву зони IANA (Internet Assigned Numbers Authority) у форматі Region/City, наприклад Europe/Berlin або America/Sao_Paulo. Такі назви містять правила переходу на літній час для відповідної місцевості, тому зміщення змінюється разом із місцевим часом. Назва з фіксованим зміщенням, як-от Etc/GMT+5, не змінюється залежно від пори року, а її знак інвертований порівняно з тим, як можна було б припустити. Виконайте LC_ALL=C TZ=Etc/GMT+5 date +%z — команда виведе -0500. Не використовуйте такі назви.
Чому налаштування лише одного параметра вирішує проблему наполовину
Установіть лише GENERIC_TIMEZONE — і Schedule Trigger спрацює в потрібну годину, але все, що читає налаштування операційної системи, залишиться в UTC. Code node, який викликає new Date().toString(), поверне рядок у UTC, рядки журналу контейнера матимуть часові мітки в UTC, а будь-яке ім’я файлу, сформоване за системним годинником, зміниться опівночі не в той час.
Установіть лише TZ — і станеться протилежне. docker compose exec n8n date виведе ваш місцевий час, що виглядатиме як успішне налаштування, але Schedule Trigger і далі використовуватиме America/New_York та спрацьовуватиме на шість годин раніше або пізніше від потрібної години. Саме цей варіант забирає найбільше часу, оскільки перша перевірка, яку зазвичай виконують, тепер проходить успішно.
Установіть часовий пояс workflow, а потім змініть GENERIC_TIMEZONE — workflow проігнорує цю зміну. Значення workflow має пріоритет і зберігає його, доки хтось не відкриє налаштування цього workflow. Якщо один workflow запускається в незвичну годину, а сусідні працюють правильно, майже завжди причина саме в цьому.
Перевірте час, а не робіть припущень
Порівняйте час безпосередньо на хості та в контейнері.
date
docker compose exec n8n date
docker compose exec n8n printenv TZ GENERIC_TIMEZONEПерші дві команди мають вивести однаковий час за місцевим годинником, щойно буде встановлено TZ. printenv виводить один рядок для кожної наявної змінної. Тому два рядки у виводі означають, що встановлено обидві змінні, а один рядок означає, що ви маєте справу з частково виправленим станом.
Тепер запитайте безпосередньо n8n із робочого процесу, оскільки shell контейнера не може показати часовий пояс на рівні робочого процесу. Додайте вузол Code до робочого процесу, який працює неправильно, і один раз запустіть його за допомогою Execute Workflow.
return [
{
json: {
n8n_time: $now.toISO(),
n8n_zone: $now.zoneName,
system_time: new Date().toString(),
},
},
];n8n_zone — це часовий пояс, який використовуватиме Schedule Trigger цього робочого процесу. Він уже визначений за порядком workflow-then-instance, тому безпосередньо відповідає на запитання. system_time містить власний часовий пояс контейнера, отриманий із TZ. Запустіть цей код у робочому процесі, який працює неправильно, а не в новому, оскільки налаштування на рівні робочого процесу зберігається разом із ним. Якщо ці два значення відрізняються, проблему знайдено без відкриття жодного конфігураційного файла.
Cron-вирази у вузлі Schedule Trigger
Schedule Trigger підтримує фіксовані інтервали від секунд до місяців, а також режим Custom (Cron) для інших варіантів. Cron-вираз обробляється в часовому поясі, визначеному для workflow, тому 0 6 * * * означає 06:00 у цьому часовому поясі, а не 06:00 UTC. П’ятикомпонентний вираз із crontab guru можна вставити без змін. n8n також підтримує необов’язкове поле секунд, яке в таблиці полів документації вказане першим: секунди, хвилини, години, день місяця, місяць, день тижня.
Не задавайте зміщення вручну. Запис 0 4 * * * на інстансі з часовим поясом UTC для запуску о 06:00 у Берліні буде правильним узимку, але влітку час буде неправильним на одну годину, оскільки взимку в Берліні діє UTC+1, а влітку — UTC+2. Установіть часовий пояс і вкажіть місцеву годину, яка вам потрібна.
Що перехід на літній час робить із завданням, призначеним на 02:30
Місцевий час на годиннику не є гарантованою миттю часу. Двічі на рік одна година зникає, а інша повторюється, тому це впливає на всі завдання, заплановані в цей час. Це можна перевірити за допомогою date на будь-якій Linux-системі, без використання n8n.
LC_ALL=C TZ=Europe/Berlin date -d '2027-03-28 02:30'date: invalid date '2027-03-28 02:30'Це не помилка в команді. 2027-03-28 годинник у Берліні одразу перейде з 02:00 на 03:00, тому місцевого часу 02:30 цього дня не існує, а date відмовляється перетворювати його на конкретну мить часу. Завдання, прив’язане до місцевого часу 02:30, не має моменту для запуску. Сусідні значення часу коректні: date -d '2027-03-28 01:30' визначається як CET, а date -d '2027-03-28 03:30' — як CEST.
Осінній перехід є дзеркальним відображенням цієї ситуації. 2027-10-31 годинник у Берліні повернеться з 03:00 на 02:00, тому 02:30 настане двічі.
LC_ALL=C TZ=Europe/Berlin date -d '2027-10-31 02:30 CEST' '+%s'
LC_ALL=C TZ=Europe/Berlin date -d '2027-10-31 02:30 CET' '+%s'1824942600
1824946200Це дві різні миті часу, обидві позначені як місцеві 02:30, з інтервалом 3600 секунд. Завдання, прив’язане до цього часу, або запуститься двічі, або один раз у час, якого ніхто не вибирав. Жоден із цих варіантів не підходить для розрахунку платежів або ротації резервних копій. Перенесіть розклад за межі цього інтервалу. У більшості європейських і північноамериканських часових зон ризикований діапазон — від 00:00 до 03:00 за місцевим часом.
Плануйте інфраструктуру в UTC, а людям показуйте місцевий час
Стандартний підхід розділяє дві функції часового поясу. Машинам потрібен стабільний інтервал. Людям потрібен час у зрозумілому форматі.
- Для завдань, за якими ніхто не стежить, встановіть часовий пояс робочого процесу UTC. Сюди належать резервне копіювання, прогрівання кешу, передавання журналів і формування звітів. У UTC інтервал між двома запусками завжди точно відповідає заданому, у кожен день року, оскільки в UTC немає переходу на літній час.
- Для завдань, результати яких читає людина, зберігайте розклад у UTC, а перетворення виконуйте під час відображення. Для цього достатньо одного виразу:
{{ $now.setZone('Europe/Berlin').toFormat('yyyy-MM-dd HH:mm') }}вставляє місцевий час у тіло повідомлення, тоді як тригер залишається стабільним.
Такий самий поділ діє і за межами n8n. Якщо частина автоматизації працює як сервіс і таймер systemd на VPS, її рядок OnCalendar читається в системному часовому поясі, який є четвертим годинником із власним налаштуванням. Якщо всі планувальники працюють у UTC, вам потрібно пам’ятати одне правило замість чотирьох. Це важливо і для всього, що підсумовує певний період, оскільки робочий процес AI-агента n8n, якому потрібні показники за вчора, непомітно використає різні 24 години залежно від часового поясу, у якому виконано обчислення.
Режими відмов і повідомлення, які ви побачите
Усе запускається приблизно на шість годин раніше або пізніше. GENERIC_TIMEZONE не було задано, тому застосовується вбудоване значення за замовчуванням America/New_York. docker compose exec n8n printenv GENERIC_TIMEZONE взагалі нічого не виводить. Задайте це значення та повторно створіть контейнер.
Ви відредагували файл Compose, але нічого не змінилося. Ви виконали docker compose restart, тому контейнер зберіг початкове оточення. Виконайте docker compose up -d, а потім перевірте результат за допомогою docker compose exec n8n printenv TZ.
Тригер налаштовано правильно, але часові мітки неправильні. Задано лише GENERIC_TIMEZONE. new Date() у вузлі Code усе одно читає часовий пояс операційної системи, тобто UTC. Задайте TZ з таким самим значенням і повторно створіть контейнер.
Один workflow ігнорує налаштування інстансу. У цього workflow у власних налаштуваннях указано часовий пояс, і він має пріоритет над GENERIC_TIMEZONE. Відкрийте canvas, виберіть три крапки, Settings, Timezone.
Щоденне завдання одного разу цього року виконалося двічі або пропустило день. Запланована година припадає на перехід на літній або зимовий час. Змініть цю годину або переведіть workflow на UTC.
FAQ
Чому мій тригер розкладу n8n спрацьовує не в ту годину?
Workflow використовує інший часовий пояс, ніж ви припускаєте. n8n вибирає часовий пояс workflow, якщо його задано; інакше — часовий пояс інстансу з GENERIC_TIMEZONE; інакше — вбудований часовий пояс America/New_York. Self-hosted інстанс, у якому ніхто не задав GENERIC_TIMEZONE, планує завдання за часом Нью-Йорка, а не за UTC. Тому зміщення рідко відповідає вашій власній різниці з UTC. Виконайте docker compose exec n8n printenv GENERIC_TIMEZONE. Відсутність виводу означає, що значення ніколи не задавали.
У чому різниця між TZ і GENERIC_TIMEZONE у n8n?
TZ — це часовий пояс операційної системи всередині контейнера. Він визначає, що повертає date у контейнері, які часові мітки з’являються в рядках журналу контейнера, що повертає new Date() у Code node і що бачить будь-який запущений там скрипт. GENERIC_TIMEZONE — це часовий пояс інстансу n8n. Його використовують вузли розкладу та вирази Luxon, наприклад $now. Якщо задати лише одне з цих значень, тригер може працювати правильно, але часові мітки будуть неправильними, або часові мітки будуть правильними, але тригер спрацьовуватиме із запізненням чи випередженням на кілька годин. Задайте для обох однакове значення.
Чи слід задавати часовий пояс workflow або GENERIC_TIMEZONE?
Задайте GENERIC_TIMEZONE як значення за замовчуванням для всього інстансу. Налаштування для окремого workflow використовуйте лише тоді, коли цей workflow справді належить до іншого часового поясу. Значення workflow має пріоритет над значенням інстансу. Воно також не змінюється після подальших змін у GENERIC_TIMEZONE, тому про забутий override для окремого workflow через кілька місяців важко згадати.
Що відбувається із завданням, запланованим на 02:30, коли змінюється час?
Цей місцевий час або не настає, або виникає двічі. LC_ALL=C TZ=Europe/Berlin date -d '2027-03-28 02:30' повертає date: invalid date '2027-03-28 02:30', оскільки того дня годинник у Берліні переходить з 02:00 на 03:00. 2027-10-31 той самий показник місцевого часу відповідає двом моментам, розділеним однією годиною. Не плануйте завдання на місцевий часовий проміжок від 00:00 до 03:00 або задайте для workflow UTC, а в місцевий час перетворюйте значення лише там, де їх переглядає користувач.