Runs
Listing executions and reading a run's timeline.
View as Markdownsctl run list --backup "$BACKUP_ID"
sctl run get <run-id>Listing
--backup is required, and takes the backup's ID. Runs are listed per backup rather than
workspace-wide.
RUN WORKFLOW STATUS STARTED DURATION
scheduled-018f3c2a-1754640000 PostgresBackupWorkflow Completed 2026-08-08T02:00:03Z 4m12s
scheduled-018f3c2a-1754553600 PostgresBackupWorkflow Failed 2026-08-07T02:00:02Z 38sAn empty list says so explicitly:
No runs in the last 30 days (the history window).That is not the same as "nothing ever ran". Run history is retained for 30 days, while
artifacts live under their retention policy. A backup with a 90-day retention will have
artifacts whose runs are long gone. Use sctl artifact list for the longer view.
Reading one run
sctl run get scheduled-018f3c2a-1754553600Run scheduled-018f3c2a-1754553600 (local)
produce Failed 38s
DumpPostgresActivity Failed 38s attempt 2
DumpPostgresActivity failed: pg_dump app: FATAL: password authentication failed for user "backup"
post-backup not_started
(no steps recorded)A run has up to two phases, each with its own steps, statuses, durations and attempt counts.
| Phase | Runs on |
|---|---|
produce | Your worker for a local backup, ours for a cloud one |
post-backup | Ours, always |
The step list is where a failure is actually diagnosed. "The run failed" is not
actionable; "DumpPostgresActivity, attempt 2, FATAL: password authentication failed" is.
Reading a failure
Three questions, in order:
1. Which phase? produce means the source, the credential or the machine.
post-backup means our pipeline or your delivery destinations.
2. Which step, on which attempt? A step that failed once and then succeeded is not a failure. A step that burned every attempt with the same message is a misconfiguration, not a transient fault.
3. What does the text say? For dump steps it is the tool's own stderr, truncated to 2000 characters. That is deliberately more useful than anything we could paraphrase.
| Failure | Usually means |
|---|---|
ConfigDrift | The backup is not in that worker's config.yaml |
SourceTypeMismatch | The worker's entry disagrees with the definition |
MissingTool | pg_dump or similar is not installed on the worker |
InvalidSource | A required credential field is missing, or a path is wrong |
EmptyOutput | A script source wrote nothing to $SAVED_OUTPUT |
stored N of M copies | A delivery destination failed, not the backup itself |
Watching a run
sctl backup trigger "$BACKUP_ID"
sctl run list --backup "$BACKUP_ID"There is no follow mode. Long steps report progress internally, so a slow run is alive rather
than hung, but you re-run run get to see the latest state.
A simple watch loop:
# Linux and macOS
watch -n 10 "sctl run list --backup $BACKUP_ID"# Windows PowerShell
while ($true) { Clear-Host; sctl run list --backup $env:BACKUP_ID; Start-Sleep 10 }Run IDs
| Origin | Shape |
|---|---|
| Schedule or trigger | scheduled-<backup-id>-<unix-timestamp> |
| Manual submission | manual-<uuidv7> |
The ID is stable across retries, so a run that retried is the same run and its artifact is filed under the ID you saw.
Scripting
Exit status is non-zero when a command fails, which is what a pipeline should branch on. The output is a human-readable table rather than JSON, so parse it with care:
LATEST=$(sctl run list --backup "$BACKUP_ID" | awk 'NR==2 { print $1 }')
STATUS=$(sctl run list --backup "$BACKUP_ID" | awk 'NR==2 { print $3 }')
[ "$STATUS" = "Completed" ] || exit 1The table format is not a stable interface. For anything you depend on, call the API and read the JSON.