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
OptionDefaultDescription
timezoneserver defaultIANA 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_orTrueStandard cron semantics: day-of-month and day-of-week match with OR. Set False to require both.
activeTrueDeploy with active=False to register a schedule without enabling it.
startnoneFirst fire at or after this instant. A date, datetime, or ISO 8601 string; a bare date is the start of that day in timezone.
endnoneNo 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 schedule

rebase 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.

On this page