Timezone-Aware Scheduling#
Overview#
Dokploy supports per-schedule timezone configuration for cron jobs. Each schedule stores an optional timezone field (an IANA timezone string); when absent, the runtime defaults to UTC. The server-side clock display (TimeBadge) surfaces the host server's current time and timezone offset so operators can reason about when scheduled jobs will fire.
Data Model#
The schedules table includes a nullable timezone text column . The four schedule types (application, compose, server, dokploy-server) all share this column . The Zod insert/update schemas are auto-derived via drizzle-zod, so timezone is optional in both .
Runtime: Scheduling with Timezone#
scheduleJob in packages/server/src/utils/schedules/utils.ts is the entry point for activating a schedule in self-hosted deployments. It reads schedule.timezone, falls back to "UTC" if absent, and passes both { tz, rule: cronExpression } to node-schedule . This makes the cron expression interpreted in the configured timezone, not the OS timezone of the host.
On Cloud, the schedule() helper from the backup utils is used instead, and timezone is forwarded the same way .
When a schedule is created or updated via the tRPC router:
- Self-hosted:
scheduleJob(newSchedule)is called after DB insert; on update, the old job is cancelled viaremoveScheduleJobbefore re-registering with the new config . - Cloud: when updating an enabled schedule,
updateJob({ ... timezone })is called. This function looks up the currently registered repeatable job pattern viagetJobRepeatable, removes it (including thetimezoneparameter to match the BullMQ repeat key), then re-adds it with the new cron/timezone configuration. On disable or delete,removeJob({ ... timezone })is called; thetimezoneparameter is required to correctly match the BullMQ repeat key (name:jobId:endDate:tz:pattern) since the job was registered withtz: job.timezone || "UTC".
UI: Timezone Picker#
The schedule create/edit form (handle-schedules.tsx) exposes an optional timezone field rendered as a searchable popover . Timezone options are drawn from a curated TIMEZONES object of 459 IANA timezone entries grouped by region (Africa, America, Asia, Europe, Pacific, etc.) defined in timezones.ts . A tooltip on the field communicates that UTC is used by default when no timezone is selected . The getTimezoneLabel() helper returns "UTC (default)" when the field is empty .
UI: Server Time Display (TimeBadge)#
TimeBadge is a client component that shows the host server's current time alongside its IANA timezone name and UTC offset. It works as follows:
- Fetches
{ time, timezone }fromapi.server.getServerTimeon mount . - Ticks locally with
setInterval(+1s)so the display stays live without polling . - Formats the time using
Intl.DateTimeFormatin the server's timezone . - Computes the UTC offset via locale-string arithmetic and renders e.g.
America/New_York | UTC-05:00. - Returns
null(renders nothing) if eithertimeortimezoneis missing — i.e., on Cloud .
The getServerTime tRPC procedure returns null on Cloud and otherwise returns new Date() plus Intl.DateTimeFormat().resolvedOptions().timeZone (the Node.js process timezone) .
TimeBadge is placed in the shared sidebar/breadcrumb layout, giving users ambient awareness of server time when configuring cron schedules.
Key Source Files#
| File | Purpose |
|---|---|
packages/server/src/db/schema/schedule.ts | DB schema — timezone column definition |
packages/server/src/utils/schedules/utils.ts | scheduleJob — wires timezone into node-schedule |
apps/dokploy/server/api/routers/schedule.ts | tRPC router — create/update/delete with job lifecycle |
apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx | Timezone picker form field (lines 507–588) |
apps/dokploy/components/dashboard/application/schedules/timezones.ts | 459 IANA timezone entries + getTimezoneLabel helper |
apps/dokploy/components/ui/time-badge.tsx | TimeBadge — live server time display (self-hosted only) |
apps/dokploy/server/api/routers/server.ts | getServerTime procedure powering TimeBadge |