> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sesameterminal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 3. Backups

# 3. Backups

> **In short.** The app makes a backup on the server every 6 hours. A backup on the same disk
> is lost if the server is lost. On this page, you send an encrypted copy of each backup to
> your backup machine each day. Then you test a restore. At the end, you have backups in two
> places, and you know that a restore works.

Each command block says where to type it: on **the server**, on **your computer**, or on
**the backup machine**.

## Step 1. Create the backup encryption key

Do this step on a trusted machine that has `age` installed. Do not use the server or a
machine that agents use.

1. Create the key:

   ```sh theme={null}
   age-keygen -o sesame-backup.agekey
   ```

2. The command shows `Public key: age1...`. Copy the part that starts with `age1`. You need
   it in Step 2.

3. Keep the file `sesame-backup.agekey` offline in two places: on an encrypted USB drive
   and in your password manager.

4. Keep it with the session secret from
   [First deploy, Step 3](2-first-deploy.md#step-3-create-the-settings-file).

You need the key file and the session secret only to restore a copy from the backup
machine.

WARNING: Do not put `sesame-backup.agekey` on the server. If you lose it, you cannot
decrypt the copies on the backup machine.

## Step 2. Turn on encryption on the server

1. On **the server**, create the file for the public key:

   ```sh theme={null}
   sudo install -o root -g root -m 0644 /dev/null /etc/sesame/backup-recipients.txt
   ```

2. Write the public key from Step 1 into the file:

   ```sh theme={null}
   echo '<the age1 public key>' | sudo tee /etc/sesame/backup-recipients.txt
   ```

3. Turn on the backup timer:

   ```sh theme={null}
   sudo systemctl enable --now sesame-seal-backups.timer
   ```

Each hour, the server encrypts each new backup with the public key. The server cannot
decrypt the backups.

**Is it working?** On the server, `systemctl list-timers sesame-seal-backups.timer` shows the
time of the next run.

## Step 3. Let the backup machine read the encrypted copies

1. On **the backup machine**, create a key without a passphrase. The key has no passphrase
   because the copy runs without a person.

   ```sh theme={null}
   ssh-keygen -t ed25519 -N "" -C sesame-backup-pull -f ~/.ssh/sesame_backup
   ```

2. On **the backup machine**, show the public key. Copy the line that it shows:

   ```sh theme={null}
   cat ~/.ssh/sesame_backup.pub
   ```

3. On **the server**, open the key file of the backup account:

   ```sh theme={null}
   sudoedit /etc/ssh/authorized_keys/sesame-backup
   ```

4. Write one line: the start below, then a space, then the public key that you copied. Save
   the file and close the editor.

   ```text theme={null}
   restrict,from="<backup-ip>",command="/usr/bin/rrsync -ro /var/lib/sesame-backup/outbox" ssh-ed25519 AAAA... sesame-backup-pull
   ```

5. On **the backup machine**, get a copy of the repository:

   ```sh theme={null}
   git clone <repo-url> <repo-folder>
   ```

6. Copy your list of allowed signers from your computer to the backup machine. Use the same
   path: `~/.config/sesame/allowed_signers`. The list is from
   [Prepare the server, Step 8](1-prepare-server.md#step-8-set-up-release-signing-and-sign-the-first-release).

7. On **the backup machine**, create the file `<repo-folder>/deploy/.deploy.env` with these
   three lines:

   ```dotenv theme={null}
   SESAME_DEPLOY_HOST=<your-domain>
   SESAME_BACKUP_KEY=~/.ssh/sesame_backup
   SESAME_ALLOWED_SIGNERS=~/.config/sesame/allowed_signers
   ```

8. On **the backup machine**, create the folder for the copies:

   ```sh theme={null}
   sudo install -d -o "$USER" -m 0700 /srv/sesame-backups
   ```

9. On **the backup machine**, connect to the server once to record its fingerprint:

   ```sh theme={null}
   ssh -i ~/.ssh/sesame_backup sesame-backup@<your-domain>
   ```

10. Compare the fingerprint with the one from
    [Prepare the server, Step 12](1-prepare-server.md#step-12-record-the-servers-fingerprint).
    If the two are the same, type `yes`. If they are different, type `no` and stop.

    After `yes`, the server shows an error and closes the connection. This result is
    correct, because the backup account can only copy files.

11. On **the backup machine**, in `<repo-folder>`, get the release tags and copy the backups
    once:

    ```sh theme={null}
    git fetch origin 'refs/tags/release-*:refs/tags/release-*'
    task deploy:pull-backups -- /srv/sesame-backups
    ```

    For each new file, the command shows `deploy: pulled <file name>`. The first time, the
    list can be empty if the timer did not run yet.

12. On **the backup machine**, find the folder of `task`:

    ```sh theme={null}
    command -v task
    ```

    The command shows a path such as `/usr/local/bin/task`. The folder is the part before
    `/task`.

13. Open the schedule of your user on **the backup machine**:

    ```sh theme={null}
    crontab -e
    ```

14. Add these two lines. Make sure that the `PATH` line contains the folder from item 12.
    If it does not, add the folder and a `:` at the start of the value. In the second line,
    replace `<repo-folder>` with your path. Save the file and close the editor.

    ```cron theme={null}
    PATH=/usr/local/bin:/usr/bin:/bin
    15 4 * * * cd <repo-folder> && git fetch -q origin 'refs/tags/release-*:refs/tags/release-*' && task deploy:pull-backups -- /srv/sesame-backups >>"$HOME/sesame-backup-pull.log" 2>&1
    ```

    The copy runs each day at 04:15. Its messages go into `sesame-backup-pull.log` in your
    home folder.

The copy only adds new files. It does not change or delete a file. It writes a checksum of
each new file into `SHA256SUMS` in the same folder. When you do not need an old copy, delete
it yourself.

**Is it working?**

1. On **your computer**, make a backup now:

   ```sh theme={null}
   task deploy:backup-now
   ```

   The command shows a file name such as `sesame-20261001T040000Z.db`, and
   `sesamectl: sealed` lines for the new files.

2. On **the backup machine**, copy the backups:

   ```sh theme={null}
   task deploy:pull-backups -- /srv/sesame-backups
   ```

3. Look in `/srv/sesame-backups`. It contains `sesame-20261001T040000Z.db.age` and
   `sesame-20261001T040000Z.db.mac.age`, with the time of your backup in the names.

## Step 4. Test a restore

Do this test once now, so that you know that a restore works. A restore stops the app for
a short time and logs out each browser.

1. On **your computer**, make a backup:

   ```sh theme={null}
   task deploy:backup-now
   ```

2. Write down the file name that the command shows, for example
   `sesame-20261001T040000Z.db`.

3. In the app, set a price alert.

4. On **your computer**, restore the backup from item 1:

   ```sh theme={null}
   task deploy:restore -- <file name>
   ```

5. The command asks `restore <file name> over the database on <your-domain>? sesame stops
   meanwhile [y/N]`. Type `y`.

6. The command shows `MAC verified` and `login sessions cleared`, then the app starts again.

7. Log in to the app again.

8. Look at your alerts. The alert from item 3 is not there, because the backup is older
   than the alert.

If the restore fails, the app stays stopped. The output says if the data changed. To start
the app again, type `task deploy:start` on your computer. [Day to day](day-to-day.md) tells
you how to restore a backup later.

Previous: [2. First deploy](2-first-deploy.md) · Next: [4. Go live](4-go-live.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.