Schedule a job
workloads: nightly-dump: role: job image: postgres:17 command: ["sh", "-c", "pg_dump \"$POSTGRES_URL\" | gzip > /backups/$(date -u +%F).sql.gz"] data_effect: none needs: [postgres] volumes: [{name: backups, path: /backups}] schedule: {cron: "0 2 * * *", timezone: Europe/Berlin}It becomes a host timer
Section titled “It becomes a host timer”A schedule is translated into a timer on the host. It fires without any
Onebox process running and survives a reboot. There is no daemon, no listening
port, and nothing to keep alive.
That design decision has a visible consequence elsewhere: encrypted environment entries are decrypted into the release when it is staged and stay there, because a timer firing at 02:00 must resolve the values the deploy resolved.
Cron is translated exactly, or refused
Section titled “Cron is translated exactly, or refused”0 2 * * * ✓ every day at 02:000 2 * * 1 ✓ every Monday at 02:000 2 1 * 1 ✗ schedule_untranslatableThe last one declares a day-of-month and a day-of-week. Cron treats that as “either matches” — it fires on the 1st and on every Monday. Onebox refuses it at load rather than running on days nobody chose.
timezone takes an IANA zone name and defaults to UTC.
Jobs still declare a data effect
Section titled “Jobs still declare a data effect”data_effect is required on every job, scheduled or not. A nightly report is
none; a nightly prune is destructive. The rollback and abort gates read it,
and a job that lies about it defeats them.
Running one by hand
Section titled “Running one by hand”ob job plan nightly-dump --out ob-job-plan.jsonob approve --plan ob-job-plan.json --out ob-job-approval.jsonob job run --plan ob-job-plan.json --approval ob-job-approval.jsonob job plan accepts a job only when its resolved when is manual, which is
the default for a job that declares none. A pre_release or post_release job
is refused: it belongs to the deploy graph, and one-shot invocation is reserved
for the jobs that do not.
A declared when: manual job remains in the digest-pinned release runtime but
never joins the deploy graph. Its job plan binds the current serving release,
runtime digest, immutable image, data effect, target, and expiry. Automation
supplies the saved plan and its separately recorded local confirmation;
migration jobs may also need the exact plan-bound backup report.
For an undeclared emergency command, the escape hatch remains:
ob exec --reason "incident investigation" <workload|service> -- <command>For a job that participates automatically in a deploy, choose a release phase:
when: pre_release # or post_release; manual is invoked only through job plan/runschedule and when are orthogonal, and both fire. A job declaring schedule:
and when: pre_release runs on the host timer at its cron time and again
on every deploy. Declaring both is how you ask for both; for the timer alone,
leave when at its manual default.
See workloads for every field.