Skip to content

Schedules

Schedules let you run saved queries on a cron schedule with timeout control, timezone support, run history, and automatic alerting on failures and slow executions.

Scheduled query jobs are a Pro feature (a license with the enterprise edition unlocks them too). Model and pipeline schedules are separate and part of the free core; see Models.

Enabled schedules keep running during the 14-day grace period after a license expires. After grace they pause, nothing is deleted, and they resume when a license is activated. Runs missed while paused are skipped, not run late: each schedule waits for its next slot. See What happens at expiry.

Anyone signed in can list schedules and read their run history. Creating, editing, deleting and manually running a schedule needs the admin or analyst role; viewers get 403.

Schedules belong to a connection. The list, editing, deleting, run history and Run now only cover the schedules of the connection you are signed in to, for admins too. A schedule on another connection answers 404 until you switch to that connection. An old schedule with no connection on it or on its saved query counts as the current connection, so it can still be seen and deleted.

Each schedule has:

  • A saved query (the SQL to execute)
  • A cron expression (5-field standard format)
  • A timezone (IANA, default UTC)
  • A timeout (milliseconds, default 60s)

Navigate to Schedules in the sidebar and click Create Schedule. The Query jobs section lists schedules as a table (name, saved query, connection, cron, timezone, last and next run) with an Enabled switch on each row; click a row to edit it.

A new schedule runs on the connection you are signed in to, whichever connection its saved query was saved on; the form says which. The API takes no other: a connection_id in the create or update body that is not the session’s connection gets 400 (Scheduled jobs can only use the connection you are signed in to. Switch to the other connection first.).

Field Description Default
Name Schedule identifier Required
Saved Query Select from existing saved queries Required
Cron 5-field cron expression Required
Timezone IANA timezone the cron is read in (e.g. America/New_York); unknown names are rejected UTC
Timeout (ms) Max execution time 60000 (60s)
Enabled Whether the scheduler should trigger this true

Standard 5-field format:

Field Range Special
Minute 0-59 *, ,, -, /
Hour 0-23 *, ,, -, /
Day of month 1-31 *, ,, -, /
Month 1-12 *, ,, -, /
Day of week 0-7 (Sun=0 or 7) *, ,, -, /

Names (MON, JAN) and shortcuts (@daily) are not supported; use numbers.

When both day of month and day of week are restricted (neither starts with *), a run happens on days that match either one, as in standard cron. 0 9 1 * 1 runs on the 1st of the month and on every Monday. When either starts with *, both must match.

Examples:

0 9 * * * # Daily at 9:00 AM
0 */6 * * * # Every 6 hours
30 2 * * 0 # Sundays at 2:30 AM
0 9 * * 5-7 # Friday to Sunday at 9:00 AM
0 0 1 * * # First of month at midnight

The cron is read as wall-clock time in the schedule’s timezone, so 0 9 * * * in Europe/Madrid runs at 09:00 Madrid time all year, across daylight saving changes. next_run_at is computed automatically and always stored in UTC.

The schedule runner wakes every 30 seconds and checks for enabled schedules with next_run_at <= now.

Up to 3 schedules run at the same time (system-wide). Each tick starts due schedules in the background and returns, so a long run never holds up the others: the ticker keeps firing every 30 seconds and starts whatever else is due. A schedule never overlaps itself; while a run is in flight, that schedule is skipped until the run ends. When all 3 slots are busy, other due schedules start on a later tick.

Scheduled runs need ClickHouse credentials. The runner:

  1. Uses the connection set on the schedule (the one it was created on), or for older schedules without one, the saved query’s connection
  2. Resolves credentials from that connection’s schedule background account: a dedicated ClickHouse account, or in the default session mode a borrowed active session (up to 3 recent sessions are tried)

If the worker is disabled, the saved account cannot be decrypted, or no session is available in session mode, the run is recorded as an error. A configured account never falls back to a human session.

  1. Fetch the linked saved query
  2. Verify the tunnel is online for the target connection
  3. Resolve credentials (see above)
  4. Execute the query with the configured timeout (minimum enforced: 60s)
  5. Record the result (status, elapsed time, rows affected, error)
  6. Update last_run_at, last_status, last_error
  7. Recompute next_run_at if the schedule is still enabled
  8. Create audit log entry

Trigger a schedule immediately without waiting for the cron:

POST /api/schedules/{id}/run

Returns synchronously with the run result including status, elapsed time, and any error.

A manual run uses your own ClickHouse account, not the background account, so it still works when the schedule worker is disabled. It only works on the connection you are signed in to, so your password is never sent to another ClickHouse server; the audit entry records the connection.

Schedules automatically create alert events for failures and slow executions. These integrate with the Governance alerts system.

Field Value
Event Type schedule.failed
Severity error
Fingerprint schedule:{id}:error

Created immediately when a scheduled run fails.

Field Value
Event Type schedule.slow
Severity warn
Fingerprint schedule:{id}:slow
Threshold Elapsed time >= 80% of the timeout (60 seconds when no timeout is set), minimum 1 second

Created when a run succeeds but takes too long relative to its timeout. Helps identify schedules that may need timeout increases or query optimization.

Both alert types use fingerprint-based deduplication to prevent duplicate alerts per schedule within the configured cooldown window.

In the app, the View runs row action opens a sheet with that schedule’s runs, paged. Click a run to see its details.

The API returns the same history:

GET /api/schedules/{id}/runs?limit=50&offset=0

Default limit: 50, max: 500.

Field Description
id Run identifier
started_at When execution began
finished_at When execution completed
status running, success, or error
rows_affected Number of rows affected
elapsed_ms Execution time in milliseconds
error Error message (if status is error)
Method Path Purpose
GET /api/schedules List the schedules of the session’s connection
GET /api/schedules/{id} Get schedule details
POST /api/schedules Create schedule
PUT /api/schedules/{id} Update schedule
DELETE /api/schedules/{id} Delete schedule
POST /api/schedules/{id}/run Manual execution
GET /api/schedules/{id}/runs Run history