Bun Just Got a Built-in Task Scheduler
Bun v1.3.11 dropped on March 18, 2026, and the headline feature is Bun.cron — a built-in API for registering cron jobs directly at the OS level. No more manually editing crontab or wrapping third-party libraries in a long-running process. Bun handles it for you — cross-platform, on Linux, macOS, and Windows.
Also in this release: a 4 MB smaller binary, Bun.sliceAnsi for proper ANSI-aware string slicing in terminal output, and rest syntax performance improvements.
The Cron Problem in JS-Land
Scheduling tasks in the Node.js ecosystem has always been a trade-off:
- System crontab works reliably, but lives separately from your project code. A new team member has no idea there are 12 cron jobs on the server until they run
crontab -l. - Libraries like
node-cronare convenient, but if the process dies, all your scheduled tasks die with it. - External schedulers like Kubernetes CronJob or AWS EventBridge are powerful but overkill for simple tasks and lock you into specific infrastructure.
The idea behind Bun.cron is different: define a task in code → Bun registers it in the OS’s native scheduler → the task runs even after the Bun process exits.
Bun.cron: How It Works
Registering a Task
// register-jobs.ts
Bun.cron("./scripts/cleanup-logs.ts", "0 3 * * *", "cleanup-logs");
Three arguments:
- Script path — which file to run.
- Cron expression — when to run it. In this example,
0 3 * * *means “every day at 3:00 AM.” - Name — an identifier for the task, so you can find or remove it later.
After execution, Bun creates an entry in the system scheduler:
- Linux → adds to crontab
- macOS → creates a launchd service
- Windows → creates an entry in Task Scheduler
Validating Cron Expressions
const result = Bun.cron.parse("*/15 * * * *");
console.log(result);
// { minute: "*/15", hour: "*", dayOfMonth: "*", month: "*", dayOfWeek: "*" }
// And if the expression is invalid:
try {
Bun.cron.parse("banana * * *");
} catch (e) {
console.error("Invalid cron expression:", e.message);
}
Bun.cron.parse() validates the expression before registration. Especially useful when schedules come from user input or configuration files — better to catch the error now than discover it at 3:00 AM.
Removing a Task
Bun.cron.remove("cleanup-logs");
Removes the task from the OS scheduler by name. Clean and predictable.
Example: Registering Multiple Jobs
// setup-cron.ts — registering three tasks in one script
const jobs = [
{ script: "./jobs/db-backup.ts", cron: "0 2 * * *", name: "db-backup" },
{ script: "./jobs/send-digest.ts", cron: "0 8 * * 1", name: "weekly-digest" },
{ script: "./jobs/cleanup-temp.ts", cron: "*/30 * * * *", name: "temp-cleanup" },
];
for (const job of jobs) {
// Validate the cron expression first
Bun.cron.parse(job.cron);
// If valid — register with the OS
Bun.cron(job.script, job.cron, job.name);
console.log(`✅ Registered: ${job.name} (${job.cron})`);
}
In this example: database backup every night at 2:00 AM, a digest email every Monday at 8:00 AM, and temp file cleanup every 30 minutes.
Cloudflare Workers Compatibility
The API is designed to be compatible with Cloudflare Workers Cron Triggers. This means if you write a scheduled task for local Bun and later want to deploy it to the cloud, the code changes will be minimal. One codebase, two environments.
4 MB Smaller Binary
The Bun team removed CMake from their build process. Result: the Linux x64 executable is 4 megabytes smaller.
4 MB might not sound like much, but in the context of Docker containers and edge servers, every megabyte counts. Smaller binary = faster Docker image pulls, lower cold start time, and a smaller footprint on IoT devices.
This isn’t the feature that’ll make you upgrade. But it’s a sign of engineering discipline — the team optimizes not just the runtime, but the process of building the runtime itself.
Bun.sliceAnsi: String Slicing for CLI Tools
If you build CLI tools, you know the pain: a terminal string isn’t just text. It’s a mix of visible characters, ANSI escape sequences, and grapheme clusters.
The standard String.prototype.slice() cuts by code points and breaks both color codes and emoji. Bun.sliceAnsi fixes this:
import { sliceAnsi } from "bun";
const colored = "\x1b[31mHello\x1b[0m 🌍 World";
// Standard slice breaks the color codes:
console.log(colored.slice(0, 10)); // Garbled invisible escape codes
// Bun.sliceAnsi respects visual width:
console.log(sliceAnsi(colored, 0, 5)); // "Hello" (red, with proper ANSI closing)
Perfect for:
- Terminal tables with colored cells
- Progress bars with emoji
- Text truncation for fixed-width columns
Rest Syntax Improvements
Bun v1.3.11 optimized the internal handling of rest syntax. This isn’t a new feature — it’s a performance improvement: operations with ... are faster and use less memory.
// These patterns are now more efficient:
const [first, ...rest] = largeArray;
const { id, ...metadata } = bigObject;
function log(message: string, ...args: unknown[]) { /* ... */ }
If your code heavily uses destructuring with rest — you get a free performance boost after upgrading.
Anti-Patterns: What NOT to Do
❌ Registering cron jobs without validation
// Bad: if the expression is invalid, you'll only find out when the task was supposed to run
Bun.cron(script, userInput, name);
// Good: validate before registering
Bun.cron.parse(userInput); // throws if the expression is invalid
Bun.cron(script, userInput, name);
❌ Duplicating task names
If you register two tasks with the same name, one silently overwrites the other. Use unique, descriptive names.
❌ Confusing Bun.cron with in-process timers
Bun.cron registers a task with the OS — it runs as a separate process on schedule. If you need a timer inside a long-running process (e.g., refreshing a cache every 30 seconds), use setInterval or Bun.sleep, not Bun.cron. Cron is for tasks that should survive your application stopping.
❌ Forgetting to clean up
If you stop using a task, remove it via Bun.cron.remove(). Otherwise it stays in the system scheduler and keeps running even after you delete the code. A classic “ghost task” scenario.
What to Do Next
- Upgrade Bun:
bun upgrade— verify you’re on v1.3.11 or newer. - Audit your scheduled tasks: check what currently lives in
crontab -lor equivalent schedulers. Which tasks are good candidates for Bun.cron? - Pilot migration: pick one non-critical task, rewrite it with Bun.cron, verify it appears in the system scheduler.
- CLI tools: if you have a Bun-based CLI with colored output, try
Bun.sliceAnsiinstead of custom solutions. - Benchmark performance: if you have hot code paths with heavy destructuring, compare speed before and after the upgrade.
Sources: