Skip to content
Dashboard
Create 5 min read

Cron Jobs

Run a finite command on a UTC schedule.

Cron Jobs#

A Cron Job starts a command on a schedule, performs a finite task, and exits. That finality is the whole point of the service type: the platform measures success by exit code, and the dashboard records per-run history rather than watching for a long-lived process.

Use Cron Jobs for reports, cleanup, data imports, periodic credential rotation, cache warming, newsletter generation, and other synchronization tasks — anything that is naturally described as "run this command at midnight, and when it is done, it is done." For a process that must stay online continuously, choose a Background Worker. For an on-demand container task that already carries a formal timeout and retry limit, choose a Workflow.

When to use — and when not to#

Use a Cron Job when:

  • The task is bounded by time rather than by a queue: nightly email exports, hourly metric rollups, daily provider syncs, weekly pruning.
  • The natural success signal is an exit code — 0 means done, non-zero means the run failed and should be retried or investigated.
  • The schedule is reasonably described as a cron pattern, not as an event.

Do not use a Cron Job when:

  • The process must stay alive continuously, consuming from a queue or stream — use a Background Worker.
  • The deliverable is an HTTP service — use Web Service or Frontend App.
  • The task has nuanced input-by-input retry and timeout expectations better described as a bounded container invocation — use a Workflow.

Schedule and command#

Choose Cron Job in New Project, configure a supported source or image, set the command, and enter a standard five-field cron expression. NexHost schedules cron jobs in UTC, so convert business-time requirements before saving the schedule.

The canonical fields — with no extension:

text
*  *  *  *  *
│  │  │  │  └─ day of week (0–7, both 0 and 7 mean Sunday)
│  │  │  └──── month (1–12)
│  │  └─────── day of month (1–31)
│  └────────── hour (0–23, UTC)
└───────────── minute (0–59)

Practical examples at the dashboard boundary (UTC):

You wantCron expressionNote
Every hour at the top of the hour0 * * * *Good probe for "is the pipeline still progressing?"
Daily at 02:00 UTC0 2 * * *Convert your local-time requirement to UTC — e.g. "02:00 Europe/Berlin in UTC" means a different hour in winter versus summer due to DST.
Every Monday at 06:15 UTC15 6 * * 1Great for weekly rollups; verify the day mapping against the help tooltip for the dashboard.
Every 10 minutes*/10 * * * *High-frequency polling belongs in a Worker if the cadence is about "keep reading," not "run this discrete report."
Warning

The platform makes the decision on UTC. Do not author the expression for your wall-clock time zone and assume it will track local daylight changes — convert once, label it in the schedule’s description if your process description holds that context, and verify the first invocation’s logged UTC timestamp matches the converted time.

The command itself follows the same source conventions as other runtime services: for a repository/archive source the dashboard expects a command string that makes sense at the project’s root (for example python scripts/cleanup.py or node scripts/import.js). For an existing-image source, the image’s entrypoint may be the command, with these settings as its arguments. Inspect the deployment detail after the first scheduled run — some images expose the effective CMD as it was assembled, which is faster than re-deriving it from Dockerfile history.

Good job behavior#

A well-behaved cron job is one your future on-call can diagnose from one run’s output and can safely retry after a partial failure.

  • Make each run safe to retry. Structure the operation to be idempotent — re-running the same command with the same input should not double-count, double-bill, or synthesize duplicate side effects. Prefer "process every unprocessed row" over "process row N" where possible, and guard external mutations with idempotency keys.
  • Exit with a non-zero code on failure. The platform reports exit status per run in history. A job that exits 0 even when the write failed reads as successful in history and will not draw attention when it silently stops delivering value. Let expected errors bubble to a non-zero exit.
  • Write actionable application output. Print what the run consumed, what it produced (count of processed items, skipped items, and failures), and where a retried run would start. Future diagnostics — and the diff between two runs — benefit more from that ledger than from verbose debug noise.
  • Keep per-run dependencies lean. A nightly import that hits an external provider once at startup and then reuses the connection is cheaper than one that establishes N connections inside N iterations. In low-resource contexts this is also friendlier to shared-host limits.

Use a Background Worker for queue consumers or any process that should not exit after a single run — a worker that has a schedule to retry after a transient error long before the next cron tick belongs on a continuous process.

Tip

Simulate the schedule locally with a single manual run: cron_expression="0 2 * * *" python scripts/cleanup.py --dry-run or your language’s equivalent scheduler harness. Observing idempotent output on a second run in the same checkout outs the class of bugs that midnight-only invocation would hide.

Observing runs#

The deployment detail / service history records each cron invocation, its inputs (per the source that was recorded for the deployment), the command as configured, the exit code, and the captured application output. Inspect a failed run before retrying so a configuration or input problem is not repeated automatically — especially for destructive jobs such as cleanup or migration, where re-running the same bad inputs compounds harm.

  • Background Workers — continuous consumers when "staying alive" is the correct model.
  • Workflows — on-demand bounded container tasks with explicit timeout and retry limit.
  • Logs — reading stage output and application lines for a scheduled rather than an HTTP-triggered service.
  • Deployments — per-run provenance and comparison after the next run.