Guides

Cron jobs

Call a path in your app automatically on a schedule.

A cron job sends a GET request to a path in your app on a schedule, such as "call /api/cleanup every day at 5 AM". Register jobs in the console, through the Cron jobs API, or declare them in nitroship.json (or vercel.json) at the deployment root.

nitroship.json
{
  "crons": [
    {"path": "/api/cron?source=scheduled", "schedule": "0 5 * * *"}
  ]
}

This example calls /api/cron?source=scheduled every day at 05:00 UTC.

Runs on production only

Cron jobs only call the current ready production deployment; previews never run them. Changes to nitroship.json require a production deploy. Console/API changes apply immediately without deploying.

Managing crons from the console

Open your app's App Settings > Cron Jobs section to add, edit, or remove jobs, then save the list. Changes apply immediately to the current production deployment. You can also replace the complete list with PUT /v1/apps/{appId}/crons; sending {"crons": []} removes all jobs.

Console/API editing is always allowed, even when nitroship.json declares crons. The saved list wins until the next production deployment that declares crons. On every such deploy, the file's list overrides console/API settings again; redeploying, promoting, or rolling back to a deployment that declares crons also restores its list.

  • An explicit "crons": [] declares an empty list and overrides console jobs too.
  • A deployment that does not declare crons uses the saved console list.
  • To hand control back to the console, remove the crons key from nitroship.json and deploy. Removing a job from the console alone does not remove it from the file.

The same rules apply to vercel.json. You can save jobs before your first production deployment, but they will not run until a ready production deployment exists.

Writing a schedule

The time zone is always UTC, and a schedule has five fields: minute, hour, day of month, month, day of week.

ExampleMeaning
0 5 * * *Every day at 05:00
*/10 * * * *Every 10 minutes
0 9 * * 1-5Weekdays at 09:00
0 0 1,15 * *The 1st and 15th of every month at 00:00
  • You can use numbers, *, ranges (1-5), lists (1,15), and steps (*/10).
  • Names such as MON or JAN, aliases such as @daily, and six-field formats with seconds are not supported.
  • You cannot restrict day of month and day of week at the same time; one of them must be *.

path must start with / and may include a query string. Each app can declare up to 100 cron jobs.

When a run fails

If a run cannot happen at its scheduled time, that run is skipped and not retried automatically. If a job must not be missed, make your app catch up on missed work itself.

Accept only cron requests

Cron paths are public addresses that anyone can call. To confirm a request came from cron:

  1. Add CRON_SECRET to your production environment variables.
  2. Deploy again.
  3. Cron requests now carry Authorization: Bearer <CRON_SECRET>. Check this value in your endpoint.

Requests use User-Agent: nitroship-cron/1.0, but anyone can fake that, so do not use it for authentication.

See registered schedules

The GET /v1/apps/{appId}/crons API lists the effective schedules, their next run times and last runs, and whether their source is "config", "console", or "none". configDeclared tells you whether the current production deployment declares crons, even if a console edit currently overrides them. See Cron jobs API.

On this page