Scheduling and Cron Jobs
Run workflows on a schedule with rebase.Cron.
Workflows accept a schedule so the platform runs them automatically — the standard shape for recurring pipelines like scraping, data refreshes, and periodic forecasts.
For pipelines that should run when data arrives rather than at a fixed time — chained workflows, dataset updates, gate-closure deadlines — see Triggers and Datasets.
Schedule a Workflow
Pass a rebase.Cron to the workflow decorator and deploy:
import rebase as rb
project = rb.project("epex-uk")
@rb.step(project="epex-uk")
def scrape_bid_curves() -> dict:
...
@rb.workflow(
project="epex-uk",
schedule=rb.Cron("0 15 * * *", timezone="Europe/London"),
)
def daily_bid_curves():
return scrape_bid_curves()
rb.deploy(daily_bid_curves)The schedule is part of the deployed workflow version: deploying activates it, and redeploying with a changed (or removed) schedule replaces it.
Cron Expressions
rebase.Cron takes a standard five-field cron expression (minute hour day-of-month month day-of-week):
rb.Cron("0 15 * * *", timezone="Europe/London") # daily at 15:00 London time
rb.Cron("*/30 * * * *") # every 30 minutes
rb.Cron("0 6 * * 1-5") # weekdays at 06:00| Option | Default | Description |
|---|---|---|
timezone | server default | IANA timezone the expression is evaluated in, e.g. "Europe/Stockholm". Set it explicitly for anything tied to local market hours — daylight-saving shifts move UTC-fixed schedules. |
day_or | True | Standard cron semantics: day-of-month and day-of-week match with OR. Set False to require both. |
active | True | Deploy with active=False to register a schedule without enabling it. |
start | none | First fire at or after this instant. A date, datetime, or ISO 8601 string; a bare date is the start of that day in timezone. |
end | none | No fire at or after this instant (exclusive), same forms as start. Refused when already in the past. |
Run Only Within a Window
A schedule can declare when it begins and when it stops, so a job that only makes sense for a few weeks — polling for a sign-up window, a one-off backfill, a pilot — never needs a reminder to switch it off:
@project.workflow(
schedule=rb.Cron(
"*/5 * * * *",
timezone="Europe/Stockholm",
start="2026-09-16", # first fire at or after 00:00 Stockholm on the 16th
end="2026-10-14", # no fire at or after 00:00 on the 14th: the 13th is the last day
),
)
def poll_for_seats():
...The window is half-open, [start, end). Both bounds are optional and accept a date, a datetime, or an ISO 8601 string; a naive value is read in the cron's timezone (UTC when none is set). A start in the past simply means "already started", so redeploying a running job is harmless. An end in the past is refused — by rb.Cron on your laptop and again by the platform — because a schedule that can never fire again is almost always a stale file.
Because start and end live on the schedule, they are part of the deployed version: they travel with the code, show up in rebase workflow schedule show, and change only when you redeploy. That is the difference from schedule pause --until, which is an operator's override on top of whatever the code declared.
Between deploy and start the platform schedule is registered but every fire is skipped, exactly like a timed pause; next_run_at points at the first fire inside the window. The first fire at or after end is skipped too, and switches the platform schedule off for good — the schedule stays visible with its end, and next_run_at reads as none. Skips are skips, not failures: no run row, no alert. Each one is stamped on the workflow, though: rebase workflow schedule show reports last_skipped_at and last_skip_reason (not_started, ended, paused, or project_paused), so a schedule that ended as declared never looks like one that silently stopped firing.
Manage Schedules from the CLI
Schedules can be inspected and changed without redeploying the workflow source:
rebase workflow schedule list # all scheduled workflows with next run times
rebase workflow schedule show forecast --project energy # cron, timezone, active state, next run
rebase workflow schedule set forecast --project energy --cron "0 15 * * *" --timezone Europe/London
rebase workflow schedule set forecast --project energy --cron "*/5 * * * *" --start 2026-09-16 --end 2026-10-14
rebase workflow schedule pause forecast --project energy # stop firing, keep the schedule registered
rebase workflow schedule pause forecast --project energy --until 2026-09-15 # …and resume automatically
rebase workflow schedule pause forecast --project energy --for 2w # …or after a duration
rebase workflow schedule resume forecast --project energy
rebase workflow schedule trigger forecast --project energy # run now, outside the schedule
rebase workflow schedule clear forecast --project energy # remove the schedulerebase workflow list and the TUI also show each workflow's cron expression and any start/end window; paused and ended schedules are marked. schedule list reports the window as starts 2026-09-16, active, ends 2026-10-14, or ended 2026-10-14.
Pause vs. disable. Pausing (schedule pause, or rebase workflow pause) keeps the schedule registered but stops runs from firing — each skipped fire is a skip, not a failure. It is operational state on the workflow, not a schedule change, so it never creates a workflow version. A timed pause (--until/--for) lifts itself: the first fire at or after the expiry runs normally, and next_run_at points at that resume-time fire while the pause holds. Disabling the workflow (enabled=False) deletes the schedule registration entirely.
Environment and Secrets
Workflows accept env= and secrets=, so a scheduled pipeline carries its own
credentials — no more splitting a workflow into a workflow-plus-function purely to
borrow secret mounting. The same values reach a run started by hand
(rebase workflow schedule trigger <name> --project <project> or workflow.remote()) and an ephemeral rebase run of the file, so a credentialed
workflow can be tested without waiting for its cron:
@project.workflow(
schedule=rb.Cron("0 15 * * *", timezone="Europe/London"),
env={"MARKET": "epex-uk"},
secrets=[rb.Secret.from_name("acme-snowflake")],
)
def daily_bid_curves():
...Pass them to @project.workflow(...) or the rebase.Workflow(...) constructor (the
module-level @rb.workflow(...) decorator does not accept them). The workflow
definition stores secret references only; the values are resolved from Secret Manager
when the schedule is synced and injected into the scheduled run's environment. A
reference that cannot be read fails the schedule sync with 502 rather than deploying a
schedule that would fail at 3 a.m. As always, env= values are stored in plaintext on
the version record — never put credentials there.
Inspect Scheduled Runs
Scheduled executions are ordinary platform runs — the same listing, events, and logs as manually triggered ones. Runs fired by a schedule carry trigger_source: "schedule"; manual and API-triggered runs carry "api".
rebase run list --target-type workflow
rebase run get <run-id>
rebase run logs <run-id>To get notified when a scheduled run fails, configure a failure webhook.
Scheduling and GitOps
Schedules deploy with the workflow version, so they follow the same environment policies: in GitOps-protected environments the schedule changes ship through the PR-based deployment flow like any other source change.

