Deploy a cron job with YAML
A cron job is a container that runs to completion on a schedule, then exits. Use it for nightly batches, periodic cleanups, hourly imports — anything that runs on a clock instead of continuously.
A cron job is a container that runs to completion on a schedule, then exits. Use it for nightly batches, periodic cleanups, hourly imports — anything that runs on a clock instead of continuously.
This guide walks through the cron job YAML shape. For the flag-mode equivalent, see Deploy a cron job with flags.
Minimal example
kind: container
project: my-project
handle: nightly-reports
name: Nightly Reports
image: ghcr.io/myorg/reports:v1.0.0
type: cronjob
regions:
- falkenstein-1
workload:
cpu: "100"
memory: "256"
command: [/usr/bin/php]
args: [artisan, reports:nightly]
schedule:
schedule: "0 2 * * *"
tz: Europe/LondonApply it:
reis apply -f nightly-reports.ymlThat cron job runs every night at 2am London time.
How cron jobs differ from workers
Same kind: container, same commands, but with key differences:
- A schedule is required — the cron expression under
schedule.scheduleis the only thing that distinguishes a cron job from a never-running worker. - Runs to completion — when your command exits successfully, the run is recorded as Succeeded; on non-zero exit it's Failed. The cron job itself is always around, waiting for the next tick.
- No autoscaling, no port, no health check — none of these apply.
- Concurrency policy controls what happens if a previous run is still going when the next tick fires.
Anatomy
kind: container
project: my-project
handle: nightly-reports
name: Nightly Reports
image: ghcr.io/myorg/reports:v1.0.0
type: cronjob
registry: ghcr
regions:
- falkenstein-1
workload:
cpu: "100"
memory: "256"
env:
REPORT_BUCKET: prod-reports
secrets:
DB_PASSWORD: db-password
S3_KEY: s3-key
command:
- /usr/bin/php
args:
- artisan
- reports:nightly
- --send-email
schedule:
schedule: "0 2 * * *" # 5-field cron — 02:00 daily
tz: Europe/London # IANA timezone the schedule fires in
concurrency: Forbid # Allow, Forbid (default), or Replace
backoff_limit: 3 # retries before a run is considered failed
ttl_seconds: 3600 # keep finished run records for N seconds
successful_history: 3 # how many successful runs to keep
failed_history: 3 # how many failed runs to keepConcurrency policy
What happens at tick T+1 if the run from tick T is still running?
| Policy | Behaviour |
|---|---|
Allow | Start the new run alongside the old one |
Forbid (default) | Skip the new tick — wait for the next |
Replace | Kill the running one and start fresh |
Pick Forbid for jobs where double-running would corrupt data; Replace for jobs where freshness beats completeness.
Execution settings
| YAML path | What it controls |
|---|---|
schedule.schedule | 5-field cron expression — required |
schedule.tz | IANA timezone (default UTC) |
schedule.concurrency | concurrency policy |
schedule.backoff_limit | retries per execution (default 3) |
schedule.active_deadline | hard wall-clock cap per run, in seconds |
schedule.ttl_seconds | seconds to keep finished run records (default 3600) |
schedule.starting_deadline | skip a run if more than N seconds late |
schedule.successful_history | how many successful runs to keep (default 3) |
schedule.failed_history | how many failed runs to keep (default 3) |
Manual runs and pausing
Once the cron job is deployed, the container:run, container:suspend, container:resume, and container:runs commands let you trigger ad-hoc runs, pause the schedule, and inspect run history. See the commands reference for details.
Updating a cron job
Re-apply the same file:
reis apply -f nightly-reports.ymlReis matches by handle. Common edits — changing the schedule, the image, the retries — all happen the same way.
Related
- Deploy a cron job with flags — flag-mode equivalent
- Deploy a worker with YAML — for continuous background work instead of scheduled
- Deploy an HTTP container with YAML — for traffic-fronting services