---
title: "Backup"
description: "A definition, not a copy. What to take, from where, how often, kept how long."
url: "https://saved.sh/docs/concepts/backups"
---

A **backup** is a definition. It says what to copy, where it runs, how often, and how long
the results are kept.

**It is not the copy.** The copies are [artifacts](/docs/concepts/artifacts), and one backup
accumulates many of them over its life.

```
Backup "prod-db"
  ├── Run 2026-08-06 02:00 ──► Artifact
  ├── Run 2026-08-07 02:00 ──► Artifact
  └── Run 2026-08-08 02:00 ──► Artifact
```

## What it holds [#what-it-holds]

| Field          | Changeable | Notes                                                  |
| -------------- | ---------- | ------------------------------------------------------ |
| `name`         | Yes        | For you, not for us. Everything else references its ID |
| `kind`         | **No**     | Who connects to your data                              |
| `source_type`  | Yes        | What is being taken                                    |
| `worker`       | Yes        | Which of your machines runs it, for `local`            |
| `schedule`     | Yes        | Cron, in UTC                                           |
| `compression`  | Yes        | Off by default                                         |
| encryption key | Yes        | Applies to future artifacts only                       |
| `retention`    | **No**     | Write-once                                             |
| `delivery`     | Yes        | Where copies are written                               |

**Two fields are immutable**, and both for the same reason: they are promises other things
depend on. See [kind](#kind-decides-who-holds-your-credentials) and
[retention](/docs/concepts/retention).

## Kind decides who holds your credentials [#kind-decides-who-holds-your-credentials]

Kind is chosen at creation and cannot be changed.

| Kind     | We connect | You run a worker | Source credential lives |
| -------- | ---------- | ---------------- | ----------------------- |
| `local`  | No         | Yes              | On your machine, only   |
| `cloud`  | Yes        | No               | In our vault            |
| `manual` | No         | No               | There is no source      |

Changing kind would silently move a credential across a trust boundary, which is not something
a settings form should be able to do. To change it, create a second backup.

<Callout>
  This is the single most consequential choice you make. `local` means we hold ciphertext and
  never see your source credentials. `cloud` means we hold the credential and process your
  plaintext. Both are legitimate; they are not equivalent.
</Callout>

## Source type [#source-type]

What is being taken. `manual` backups have none.

| Type                                      | `local` | `cloud`   |
| ----------------------------------------- | ------- | --------- |
| `postgres`, `mysql`, `redis`, `s3`, `web` | Yes     | Yes       |
| `script`, `file`, `folder`                | Yes     | **Never** |

The two "never" rows are guarantees rather than gaps. We do not execute customer commands in
our cloud and your filesystem does not exist there; the drive sources run on an OAuth grant
that is ours to hold and a local worker never sees.

## States [#states]

```
draft ──► active ⇄ paused
              └──► deleting
```

| State      | Meaning                                                |
| ---------- | ------------------------------------------------------ |
| `draft`    | Created, not yet complete. Nothing is scheduled        |
| `active`   | Complete and scheduled                                 |
| `paused`   | Schedule suspended. Definition and artifacts untouched |
| `deleting` | On its way out. No longer configurable                 |

A backup becomes active on its own, as soon as its definition has everything its kind
requires. You do not activate it explicitly.

## Creating one is two steps [#creating-one-is-two-steps]

```bash
sctl backup create prod-db --kind local     # reserves the name, the kind, and an ID
sctl backup configure "$BACKUP_ID" ...      # everything else
```

The split exists because kind must be fixed before anything else can be validated against it.
A manifest hides this: [`sctl apply`](/docs/cli/apply) does both.

## Deleting [#deleting]

Refused while artifacts still exist. A definition cannot be removed out from under the things
it produced, and a locked artifact blocks it until the lock lapses.

**Pause is almost always what you want.** It stops the schedule and changes nothing else.

## Naming [#naming]

Names are for humans. Every command and API call takes the **ID**, which `sctl backup list`
prints beside the name.

Name for what fails, not for what it is: `prod-db` beats `postgres-backup-1`, because the
name is what you read at 3am while deciding what to restore.

## Next [#next]

<Cards>
  <Card href="/docs/concepts/runs" title="Run" description="What happens when a backup fires." />

  <Card href="/docs/backups" title="Backups in depth" description="Every field, and the legal combinations." />

  <Card href="/docs/backups/sources" title="Sources" description="What each source type produces." />
</Cards>
