---
title: "Quickstart"
description: "From nothing to a restorable backup of a Postgres database."
url: "https://saved.sh/docs/quickstart"
---

This walks the local path: a worker on your own machine, so the database credential never
leaves your network. It takes about ten minutes.

## 1. Create a workspace [#1-create-a-workspace]

Sign up and create one. A workspace is the tenant: members, permissions, quotas and billing
all belong to it, and nothing crosses between workspaces. No card is required, and a new
workspace starts with credit against real usage.

## 2. Install the CLI [#2-install-the-cli]

```bash
curl -fsSL https://saved.sh/cli.sh | sh
sctl login
```

`sctl login` uses a device code. It prints a code, you approve it in the browser, and the
session lands in a local config file. Every later command acts on exactly one workspace, the
one your session is scoped to.

```bash
sctl workspace list
sctl workspace switch <name>
```

## 3. Provision a worker [#3-provision-a-worker]

A worker is a **credential**, not a machine. Provisioning gives you a config file to put on
the host that can reach your database.

```bash
sctl worker provision prod-worker-1
```

Then install and run the worker binary on that host:

```bash
curl -fsSL https://saved.sh/worker.sh | sh
saved-worker --config ./config.yaml
```

<Callout type="warn">
  The worker credential is shown once. It is what lets a machine claim work in your
  workspace, so treat it like a database password, and if it leaks, rotate it with
  `sctl worker rotate` rather than deleting the worker.
</Callout>

## 4. Generate an encryption key [#4-generate-an-encryption-key]

Backups are encrypted before they leave the machine, with a key we never see.

```bash
gpg --quick-generate-key "backups@example.com" default default never
gpg --armor --export backups@example.com > ./keys/prod.asc
```

<Callout type="error">
  Keep the private half somewhere you will still have it after the incident that makes you
  need it. We hold only the public key. If you lose the private key, your backups are
  unreadable by you and by us.
</Callout>

## 5. Declare the backup [#5-declare-the-backup]

Write it in a file and apply it, rather than clicking through a form:

```yaml title="saved.yaml"
workers:
  - name: prod-worker-1

backups:
  - name: prod-db
    kind: local
    source_type: postgres
    worker: prod-worker-1
    schedule: "0 2 * * *"
    compression: true
    retention:
      keep_last: 10
      expire_after: 90d
    encryption:
      public_key: ./keys/prod.asc
```

```bash
sctl apply -f saved.yaml
```

<Callout>
  `apply` reconciles what the file declares. It never deletes what the file omits. Removing
  a backup is `sctl backup delete`, deliberately, so a typo in a filename cannot destroy a
  retention policy.
</Callout>

## 6. Run it now [#6-run-it-now]

Don't wait for 02:00 to find out whether it works.

```bash
sctl backup trigger $BACKUP_ID
sctl run list --backup $BACKUP_ID
```

## 7. Prove you can get it back [#7-prove-you-can-get-it-back]

A backup you have never restored is a hypothesis.

```bash
sctl artifact list --backup $BACKUP_ID
createdb restore_test
sctl restore postgres <artifact-id> --host localhost --database restore_test
```

Restore is client-side: the artifact is downloaded and decrypted **on your machine**, with
your private key. We are not in the path and could not be.

## Next [#next]

<Cards>
  <Card href="/docs/backups" title="Backups in depth" description="Kinds, sources, schedules and retention." />

  <Card href="/docs/recover" title="Recover" description="What to do if we are not here." />
</Cards>
