My 9 AM Workflow Ran at 1 AM: The Timezone Bug That Only Strikes in Production
Description and use
In production, an n8n workflow scheduled for 9 AM ran at 1 AM due to the container’s UTC clock, sending digests at the wrong time and with stale data. The fix is to set GENERIC_TIMEZONE to the audience’s timezone and add a guard node that logs the actual execution time.
Problem
A scheduled n8n workflow that should run at 9 AM local time executed at 1 AM UTC, sending digests to clients at the wrong time and using stale data.
Outcome
The workflow now reliably runs at the intended local time, sends digests on schedule, and uses correct data.
Workflow
- Set GENERIC_TIMEZONE=Asia/Shanghai in deployment environment
- Configure Schedule Trigger cron expression in audience’s timezone
- Add a guard node: Schedule Trigger → Set (log execution_time: {{ $now.toFormat('yyyy-MM-dd HH:mm z') }})
- Verify first real run logs the expected time
- Ensure date calculations use the pinned timezone
- Avoid scheduling between 1:00–3:00 AM in DST‑observing zones
Evidence
The Schedule Trigger was set to 9:00. The executions list said 9:00. And yet the email landed at 1:00 AM local time.
n8n evaluates Schedule Trigger cron expressions in the timezone of the machine (or container) it's running on.
I now set it explicitly in every deployment: GENERIC_TIMEZONE=Asia/Shanghai
Every scheduled workflow I ship now starts with a two-node guard: Schedule Trigger → Set (log execution_time: {{ $now.toFormat('yyyy-MM-dd HH:mm z') }})
- Record date: 2026-10-16T02:41:06.748Z