Skip to content
brought to you byVoidZero
Private Beta:Void is currently in Private Beta. We do not recommend using it for mission-critical production workloads yet. Please back up your data regularly.

Cron Jobs ​

Void supports cron-triggered jobs from a top-level crons/ directory.

Job files ​

Create files in crons/**/*.ts (.mts, .js, .mjs also supported).

Each job file must export:

  • export const cron = "<expression>" (or an array of expressions)
  • a default handler (recommended: wrapped with defineScheduled)

Example:

ts
// crons/hourly-heartbeat.ts
import { defineScheduled } from 'void';

export const cron = '0 * * * *';

export default defineScheduled(async (controller, env) => {
  await env.KV.put('jobs:last-heartbeat', controller.scheduledTime.toString());
});

Multiple schedules ​

A single job file can export an array of cron expressions:

ts
// crons/cleanup.ts
import { defineScheduled } from 'void';

export const cron = ['0 * * * *', '30 * * * *'];

export default defineScheduled(async (controller, env) => {
  // Runs at :00 and :30 every hour
});

defineScheduled ​

defineScheduled(fn) is a typed identity helper for scheduled jobs.

Handler signature:

ts
(controller: ScheduledController, env: CloudEnv['Bindings'], ctx: ExecutionContext) =>
  unknown | Promise<unknown>;

Notes:

  • Jobs are matched by exact cron string.
  • Job modules are lazy-loaded at runtime.
  • Files or directories starting with _ are ignored.
  • Missing cron export causes an error during scan/build.

Local development ​

Cron triggers do not fire on their schedule during local dev — this is a Cloudflare Workers / miniflare limitation, not specific to Void. Exercise a job locally by POSTing to the dev endpoint Void exposes:

POST /__void/scheduled
Content-Type: application/json
{ "cron": "<expression>", "scheduledTime": <unix_ms> }

The cron value must match a string you exported from a crons/*.ts file — that's how the dispatcher routes to the right handler. Returns { "ok": true } on success.

The endpoint requires a local dev trigger token. Void prints a paste-ready curl command with the current token when the dev server starts.

bash
curl -X POST http://localhost:5173/__void/scheduled \
  -H "Content-Type: application/json" \
  -H "x-void-dev-trigger: <printed-token>" \
  -d '{"cron":"0 * * * *","scheduledTime":'"$(date +%s000)"'}'

If you set __VOID_PROXY_TOKEN in .dev.vars, that explicit token takes precedence and the printed curl command uses x-void-internal: <your-token> instead.

This works the same in default Void mode and every supported framework — SvelteKit, Nuxt, Analog, Astro, TanStack Start, React Router, and vinext. In framework mode the cron handler runs inside the framework adapter's request pipeline (or the dev miniflare for Class A frameworks), so it sees whatever bindings the adapter exposes (D1, KV, R2, queues, AI, etc.).

Deployment behavior ​

On deploy, Void includes all discovered job schedules in the deploy manifest and configures worker cron triggers automatically.