Quickstart
Parse a cron expression to find the next matching time:Bun.cron.parse()
Parse a cron expression and return the next matching UTC Date.
Parameters
| Parameter | Type | Description |
|---|---|---|
expression | string | A 5-field cron expression or predefined nickname |
relativeDate | Date | number | Starting point for the search (defaults to Date.now()) |
Returns
Date | null — the next matching UTC time, or null if no match exists within ~4 years (e.g. February 30th).
Chaining calls
Callparse() repeatedly to get a sequence of upcoming times:
Cron expression syntax
Standard 5-field format:minute hour day-of-month month day-of-week
| Field | Values | Special characters |
|---|---|---|
| Minute | 0–59 | * , - / |
| Hour | 0–23 | * , - / |
| Day of month | 1–31 | * , - / |
| Month | 1–12 or JAN–DEC | * , - / |
| Day of week | 0–7 or SUN–SAT | * , - / |
Special characters
| Character | Description | Example |
|---|---|---|
* | All values | * * * * * — every minute |
, | List | 1,15 * * * * — minute 1 and 15 |
- | Range | 9-17 * * * * — minutes 9 through 17 |
/ | Step | */15 * * * * — every 15 minutes |
Named values
Month and weekday fields accept case-insensitive names:0 and 7 mean Sunday in the weekday field.
Predefined nicknames
| Nickname | Equivalent | Description |
|---|---|---|
@yearly / @annually | 0 0 1 1 * | Once a year (January 1st) |
@monthly | 0 0 1 * * | Once a month (1st day) |
@weekly | 0 0 * * 0 | Once a week (Sunday) |
@daily / @midnight | 0 0 * * * | Once a day (midnight) |
@hourly | 0 * * * * | Once an hour |
Day-of-month and day-of-week interaction
When both day-of-month and day-of-week are specified (neither is*), the expression matches when either condition is true. This follows the POSIX cron standard.
*), only that field is used for matching.
Bun.cron()
Register an OS-level cron job that runs a JavaScript/TypeScript module on a schedule.
Parameters
| Parameter | Type | Description |
|---|---|---|
path | string | Path to the script (resolved relative to caller) |
schedule | string | Cron expression or nickname |
title | string | Unique job identifier (alphanumeric, hyphens, underscores) |
title overwrites the existing job in-place — the old schedule is replaced, not duplicated.
The scheduled() handler
The registered script must export a default object with a scheduled() method, following the Cloudflare Workers Cron Triggers API:
worker.ts
async. Bun waits for the returned promise to settle before exiting.
How it works per platform
Linux
Bun uses crontab to register jobs. Each job is stored as a line in your user’s crontab with a# bun-cron: <title> marker comment above it.
The crontab entry looks like:
scheduled() handler.
Viewing registered jobs:
scheduled() handler.
Manually uninstalling without code:
macOS
Bun uses launchd to register jobs. Each job is installed as a plist file at:StartCalendarInterval to define the schedule. Complex patterns with ranges, lists, or steps are supported — Bun expands them into multiple StartCalendarInterval dicts via Cartesian product.
Viewing registered jobs:
weekly-report:
Windows
Bun uses Windows Task Scheduler with XML-based task definitions. Each job is registered as a scheduled task namedbun-cron-<title> using CalendarTrigger elements and Repetition patterns.
Most cron expressions are fully supported, including @daily, @weekly, @monthly, @yearly, ranges (1-5), lists (1,15), named days/months, and day-of-month patterns.
User context
Tasks are registered usingS4U (Service-for-User) logon type, which runs jobs as the registering user even when not logged in — matching Linux crontab behavior. No password is stored.
TCP/IP networking (fetch(), HTTP, WebSocket, database connections) works normally. The only restriction is that S4U tasks cannot access Windows-authenticated network resources (SMB file shares, mapped drives, Kerberos/NTLM services).
On headless servers and CI environments where the current user’s Security Identifier (SID) cannot be resolved — such as service accounts created by NSSM or similar tools — Bun.cron() will fail with an error explaining the issue. To work around this, either run Bun as a regular user account, or create the scheduled task manually with schtasks /create /xml <file> /tn <name> /ru SYSTEM /f.
Trigger limit
:::caution Windows Task Scheduler enforces a limit of 48 triggers per task (theCalendarTrigger element has maxOccurs="48"). Some cron expressions that work on Linux and macOS exceed this limit on Windows.
Expressions that work on all platforms:
| Pattern | Trigger strategy | Count |
|---|---|---|
*/5 * * * * | Single trigger with Repetition (PT5M) | 1 |
*/15 * * * * | Single trigger with Repetition (PT15M) | 1 |
0 9 * * MON-FRI | One CalendarTrigger per weekday | 5 |
0,30 9-17 * * * | 2 minutes × 9 hours | 18 |
@daily, @weekly, @monthly, @yearly | Single trigger | 1 |
| Pattern | Why | Trigger count |
|---|---|---|
*/7 * * * * | 9 minute values × 24 hours | 216 |
*/8 * * * * | 8 minute values × 24 hours | 192 |
*/9 * * * * | 7 minute values × 24 hours | 168 |
*/11 * * * * | 6 minute values × 24 hours | 144 |
*/13 * * * * | 5 minute values × 24 hours | 120 |
*/15 * * 6 * | Month restriction prevents Repetition: 4 × 24 | 96 |
0,30 * 15 * FRI | OR-split doubles triggers: 2 × 24 × 2 | 96 |
Repetition interval (single trigger) or must expand to individual CalendarTrigger elements. Minute steps that evenly divide 60 (*/1, */2, */3, */4, */5, */6, */10, */12, */15, */20, */30) use Repetition and work regardless of other fields. Steps that don’t divide 60 (*/7, */8, */9, */11, */13, etc.) must be expanded, and with 24 hours active, the count quickly exceeds 48.
When a pattern exceeds the limit, Bun.cron() rejects it with an error message. To work around it, simplify the expression or restrict the hour range:
Windows containers
:::cautionBun.cron() is not supported in Windows Docker containers. The Task Scheduler service is not running in servercore or nanoserver images. Use an in-process scheduler for containerized workloads.
:::
Viewing registered jobs:
bun-cron-<title>, right-click, and delete it.
Bun.cron.remove()
Remove a previously registered cron job by its title. Works on all platforms.
Bun.cron() did:
| Platform | What remove() does |
|---|---|
| Linux | Edits crontab to remove the entry and its marker comment |
| macOS | Runs launchctl bootout and deletes the plist file |
| Windows | Runs schtasks /delete to remove the scheduled task |