All pages
Scheduled jobs
A cron here is a request on a schedule. The platform calls the path at the right minute, waking the machine if it is asleep, and the machine sleeps again afterwards. A job that runs for a minute a day costs a minute a day.
Declaring one
x-pilots:
schedules:
- cron: "0 5 * * *" # five fields, UTC, or @hourly @daily @weekly @monthly
path: /jobs/digest
- cron: "@hourly"
cmd: /app/bin/tick # a command, for work with no route
Five fields and UTC, or one of the named expressions. A path job
is an ordinary request. A cmd job runs a command instead, for work that has no
route, and it runs as the app user from that user's home, so spell the path out in
full.
Or in the config your app already has
A cron needs no pilots-specific file. A vercel.json is read for
any app, whatever it is written in and whether or not it brought a Dockerfile, so a Rails
or Django service carrying one gets its crons too.
{
"crons": [
{ "path": "/api/digest", "schedule": "0 5 * * *" }
]
}
A webjs app can carry the same list in its own manifest, and that one wins where both are present.
{
"webjs": {
"crons": [
{ "path": "/jobs/digest", "schedule": "0 5 * * *" }
]
}
}
What the handler sees
A GET carrying a header that says which expression fired. The public edge strips that header from every request arriving from outside, so checking for its presence is the whole authentication, with no secret to store or rotate.
export function GET(req) {
if (!req.headers['x-pilot-cron']) return new Response('no', { status: 403 })
return runDigest()
}
What to build for
- A job can fire twice in rare cases, a host restart or a deploy inside its minute, so make it idempotent.
- A job still running when its next minute comes is skipped rather than overlapped.
- One replica fires for a service, however many replicas it has.
- A
pathjob needs a machine that can wake, soauto_start: falsetogether with the default suspend behaviour is refused at create: the job could never run. Acmdjob is not a request and takes neither. - To remove every cron, deploy with an empty list. An absent key keeps the previous release's schedules.
On a sandbox
A machine that is not a service takes a schedule of its own at create.
pilot machine create nightly --schedule "0 5 * * * GET /jobs/digest"
pilot machine create backup --schedule "@hourly /usr/local/bin/backup.sh"
| You want | Reach for |
|---|---|
| A job with a route, in a service |
schedules with a path
|
| A job with no route |
schedules with a cmd
|
| Something that must run continuously rather than on a schedule | a resident replica (services and releases) |