shiply.now

Crons

Schedule your site's worker to run on a recurring UTC schedule — daily digests, hourly polling, retention emails

Cron triggers fire on a schedule (UTC) and call your worker's scheduled() handler. Use them for daily reminders, weekly digests, hourly polling, or any recurring background job — without standing up a separate cron service.

Crons run on the same per-site worker as Functions, so they share the same bindings (env.SITE_DB, secrets, variables).

Declare schedules in .shiply/crons.json

{
  "crons": [
    { "path": "/api/cron/daily-reminder", "schedule": "0 9 * * *" },
    { "path": "/api/cron/hourly-sync",   "schedule": "0 * * * *" }
  ]
}

On shiply publish, shiply registers these as Cloudflare Worker cron triggers. The path is a label that gets passed to your handler — it's not a route the world can hit; CF dispatches scheduled events directly to your worker.

Schedule syntax

Standard 5-field crontab, UTC only:

ExpressionMeaning
0 9 * * *Every day at 09:00 UTC
*/5 * * * *Every 5 minutes
0 * * * *Top of every hour
0 0 1 * *First of each month at 00:00 UTC
0 0 * * 0Every Sunday at 00:00 UTC

Full reference: Cloudflare cron triggers.

Handler

Add a scheduled method to your worker default export. Dispatch on event.cron to handle multiple schedules in one worker:

// worker.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // ... your normal request handling
    return env.ASSETS.fetch(request)
  },

  async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext) {
    // event.cron === "0 9 * * *" — match on this to dispatch
    if (event.cron === '0 9 * * *') {
      await sendDailyReminders(env)
    }
    if (event.cron === '0 * * * *') {
      await syncFromUpstream(env)
    }
  },
}

scheduled runs in the same V8 isolate as your fetch handler with the same bindings — including any secrets and the attached D1 database (env.SITE_DB).

Manage from the CLI

shiply cron ls <slug>                              # list configured crons
shiply cron set <slug> /api/cron/daily "0 9 * * *" # add or replace one
shiply cron rm  <slug> /api/cron/daily             # remove one

Manage from MCP

ToolPurpose
list_cronsRead configured crons for a site
set_cronAdd or replace a cron trigger
remove_cronDrop a cron trigger by path

Limits

  • 20 cron triggers per site (shiply cap; Cloudflare's per-Worker limit on the underlying paid plan is higher).
  • 30 s CPU per scheduled run — same isolate budget as fetch handlers.
  • No sub-minute schedules. * * * * * (every minute) is the smallest interval; Cloudflare doesn't accept second-precision crontabs.