---
title: "Runs"
description: "Listing executions and reading a run's timeline."
url: "https://saved.sh/docs/cli/runs"
---

```bash
sctl run list --backup "$BACKUP_ID"
sctl run get <run-id>
```

## Listing [#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  38s
```

An empty list says so explicitly:

```
No runs in the last 30 days (the history window).
```

<Callout>
  **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.
</Callout>

## Reading one run [#reading-one-run]

```bash
sctl run get scheduled-018f3c2a-1754553600
```

```
Run 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 [#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 [#watching-a-run]

```bash
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:

```bash
# Linux and macOS
watch -n 10 "sctl run list --backup $BACKUP_ID"
```

```powershell
# Windows PowerShell
while ($true) { Clear-Host; sctl run list --backup $env:BACKUP_ID; Start-Sleep 10 }
```

## Run IDs [#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 [#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:

```bash
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 1
```

<Callout type="warn">
  The table format is not a stable interface. For anything you depend on, call the
  [API](/docs/api/runs) and read the JSON.
</Callout>

## Next [#next]

<Cards>
  <Card href="/docs/cli/artifacts" title="Artifacts" description="What a successful run produced." />

  <Card href="/docs/backups/runs" title="Runs in depth" description="Phases, statuses and the history window." />

  <Card href="/docs/workers/troubleshooting" title="Worker troubleshooting" description="When the produce phase is the problem." />
</Cards>
