Crons

A cron is a self-contained directory in your content root, named for the cron. It holds the code the server runs and the settings that say when.

<cronhoster-config-contentRoot>/
  example/                        one cron, named "example"
    cronhoster-cron-handler.js      the entry point the server runs
    cronhoster-cron-settings.json   its runs and concurrency
A cron is a directory with a handler and a settings file.

Contents

A cron directory holds two required files, plus any modules the handler imports.

NameTypeDescription
cronhoster-cron-handler.jsfileThe entry point. The server runs it as a fresh process on each matching run, with the matched time on the command line.
cronhoster-cron-settings.jsonfileThe schedule and settings: the named runs, and how many handlers may run at once.
anything elsefile or directoryRides along with the cron and is imported by the handler; never run on its own.

cronhoster-cron-handler.js

The handler runs as a fresh Node process, with its own directory as the working directory, so it resolves the modules beside it. It receives the matched time on the command line: the cron name, the run name, then the year, month, day, hour, minute and second.

const [cron, run, year, month, day, hour, minute, second] = process.argv.slice(2);
console.log(`${cron} / ${run} fired at ${year}-${month}-${day} ${hour}:${minute}:${second}`);
The example handler: it logs the run it is and the time it fired.

With the settings below, this cron fires twice a minute (at :00 and :30) and logs a line each time, for example example / half fired at 2026-07-25 14:05:30. Its exit code, stdout and stderr are captured per run and shown in the Cron Logs tool. The handler contract is on the Scripts reference.

cronhoster-cron-settings.json

The settings file names one or more runs. Each run is a named schedule: five time fields, each a number or *. The server reads the UTC clock once a second and fires a run when all five of its fields match (a number equals that field; * matches any value).

{
  "cronhoster-cron-setting-maxConcurrent": 1,
  "cronhoster-cron-setting-runs": [
    {
      "cronhoster-cron-setting-run-name": "top",
      "cronhoster-cron-setting-run-month": "*",
      "cronhoster-cron-setting-run-day": "*",
      "cronhoster-cron-setting-run-hour": "*",
      "cronhoster-cron-setting-run-minute": "*",
      "cronhoster-cron-setting-run-second": 0
    },
    {
      "cronhoster-cron-setting-run-name": "half",
      "cronhoster-cron-setting-run-month": "*",
      "cronhoster-cron-setting-run-day": "*",
      "cronhoster-cron-setting-run-hour": "*",
      "cronhoster-cron-setting-run-minute": "*",
      "cronhoster-cron-setting-run-second": 30
    }
  ]
}
Two runs: one at :00 and one at :30 of every minute.
NameTypeDescription
cronhoster-cron-setting-maxConcurrentinteger, optionalHow many of this cron's handlers may run at once. Defaults to 1. A run that matches while the cron is at its ceiling is skipped, never queued.
cronhoster-cron-setting-runsarrayThe named schedules. Each fires independently when the clock matches it.
cronhoster-cron-setting-run-nametextA name for the run, distinct within the cron. It is passed to the handler and shown in the Cron Logs tool.
cronhoster-cron-setting-run-month1–12 or *The month it fires in.
cronhoster-cron-setting-run-day1–31 or *The day of the month.
cronhoster-cron-setting-run-hour0–23 or *The hour, UTC.
cronhoster-cron-setting-run-minute0–59 or *The minute.
cronhoster-cron-setting-run-second0–59 or *The second.

Every field of a run is required. A missing field is an error, because it is ambiguous whether it means a wildcard or a real value. Times are always UTC. The full grammar is on the Settings reference.

Publishing and versioning

Author the cron locally, then push it with the local package. A cron directory is uploaded whole the first time it appears and is then never re-synced, so a running cron's files never change underneath it. To change a cron you publish a new directory under a new name: stage it as _example (a leading _ keeps it local while you test), then drop the underscore to go live and remove the old one.