← All guides

How to schedule SQL reports

Most teams already run a scheduler: cron on a server, Airflow, Dagster, Databricks Jobs or GitHub Actions. DRE doesn’t add another one. It keeps the schedule definitions next to the reports, shows when each one fires, and gives your scheduler one command to run, so a report runs on time and a rerun reproduces the original period.

1. Name the schedules next to the reports

schedules.yml lives in the project, in git. One report can have several schedules, each with its own variables, and a schedule can follow a group of reports by tag.

schedules.yml
- name: flash_daily
  report: sales_summary
  cron: "0 7 * * *"
  timezone: Australia/Sydney
  vars: {period: day}
- name: close_monthly
  report: sales_summary
  cron: "0 6 1 * *"
  timezone: Australia/Sydney
  vars: {period: month}
- name: finance_daily
  select: tag:finance
  cron: "0 7 * * 1-5"
  timezone: Australia/Sydney

When many schedules share a timing, such as “the 1st of the month, 06:00 Sydney”, name it once intimings.yml (with holidays to skip) and refer to it from each schedule.

2. Check when they fire

dre schedule ls works out every firing in a window, so a reviewer can check that a cron expression and timezone mean what the author thinks, before it reaches production.

terminal
dre schedule ls                                # the next 5 firings of each schedule
dre schedule ls -s sales_summary --limit 10    # when does this report run?
dre schedule ls --output json                  # every firing in the next 35 days, with its command

3. Let your scheduler run the command

Whatever the scheduler, the task is one command. Pass the instant the run was scheduled for in DRE_RUN_AT, so the report’s dates come from the schedule rather than the clock.

crontab
# min hour day month weekday
0 6 1 * *  cd /srv/reports && DRE_RUN_AT="$(date -u +\%Y-\%m-\%dT\%H:00:00Z)" dre run --schedule close_monthly --target prod

In Airflow, Dagster or Databricks Jobs, run the same command as a task, usually right after the job that refreshes the tables (for example your dbt run), with the run’s logical time as DRE_RUN_AT. For a self-hosted setup that loads every firing into a database and runs what’s due, see the orchestration recipe, which DRE’s demo project tests against Postgres.

4. Rerun a period without editing anything

When someone asks for September again because late refunds came in, run the same schedule with September’s instant. The SQL, file names and email subjects render as they did on the day:

terminal
DRE_RUN_AT=2026-09-30T20:00:00Z dre run --schedule close_monthly --target prod

Every run records its schedule, variables and scheduled instant in run_results.json and its log, so you can see exactly what ran. (Reproducing the same numbers also needs the source data as it was, if it has changed since.)

Why not a scheduler built into the reporting tool?

Your scheduler already handles retries, alerts, dependencies and access control, and your team already watches it. Keeping report schedules there means reports run after the data they depend on, failures show up where the other failures do, and there is no second system to secure. DRE’s part is to make the schedule reviewable in git and the run reproducible.