Encrypted backups with ssv and age

ssv backup scans like ssv scan, then streams the files straight into age, which encrypts them. An unencrypted copy of the archive is never written to disk. If age isn't installed, ssv backup stops before scanning and prints the commands that install it on your server; see also how to install age.

The backup contains every secret on the server, so protect it, and especially its key, accordingly.

Key-based backups can run unattended, and the key needed to decrypt them never has to be on the server.

  1. On your own computer, not the server, create a key pair. Keep the key file somewhere safe, such as a password manager or an encrypted drive:

    age-keygen -o ssv-main.key
    
    Public key: age10088g4ulpmqv7mcxtdx5q83llg0yrcgzx84czwky8fxatlyrfpcqc9d7tp
    
  2. Create a second, recovery key the same way and store it offline, separately from the first. Backups can then be decrypted with either key, so losing one doesn't lose your backups.

  3. On the server, put the two public keys (the age1... lines) in a recipients file. Public keys can only encrypt, so this file is not secret:

    sudo install -d -m 755 /etc/ssv
    sudo tee /etc/ssv/recipients.txt <<'EOF'
    age10088g4ulpmqv7mcxtdx5q83llg0yrcgzx84czwky8fxatlyrfpcqc9d7tp
    age1dsxf4ajxyxy8p03gekagw73zmc9lq3mkx5cmsjwdtwq8wkhpre0s4j09sf
    EOF
    
  4. Make a backup:

    sudo install -d -m 700 /var/backups/ssv
    sudo ssv backup --recipients-file /etc/ssv/recipients.txt --output /var/backups/ssv/web01-$(date +%Y%m%d).age
    
    Creating encrypted backup containing 6 files...
    Encrypted backup created: /var/backups/ssv/web01-20261007.age
    Files archived: 6
    No plaintext tar archive was written to disk.
    

Instead of a recipients file you can give keys directly with --recipient age1..., repeated for each key. SSH public keys (ssh-ed25519 ... or ssh-rsa ...) also work as recipients.

Passphrase instead of keys

sudo ssv backup --output web01.age

age asks for a passphrase on the terminal. If you leave it empty, age generates a strong passphrase and prints it; write it down. Passphrase mode needs someone at the keyboard, so it can't be used from cron.

How backups behave

  • Without --output, the backup is written to the current directory as serversecretvault-YYYYMMDD-HHMMSS.age.

  • The backup file is readable by its owner only (mode 0600).

  • An existing file is never overwritten; choose a new --output name instead.

  • If the backup is interrupted (Ctrl-C, or the process is stopped), the partial file is removed.

  • If a file changes, disappears or is replaced between the scan and the moment it is backed up, it is left out and reported, and the rest of the backup continues:

    Skipped /home/bob/public_html/.env: file changed after scan
    

What is inside

The backup is an age-encrypted tar archive. Each file keeps its full original path under files/, along with its owner, group and permissions. manifest.json, the last entry, records when and where the backup was made, the details of each file (as in ssv scan --json), and a skipped list of any files that were left out and why.

-rw------- alice/alice      24 2026-10-07 21:26 files/home/alice/app/.env
-rw-r----- alice/www-data   33 2026-10-07 21:26 files/home/alice/public_html/wp-config.php
-rw-r--r-- alice/alice      33 2026-10-07 21:26 files/home/alice/public_html/wp-config.php.bak
-rw-r--r-- bob/bob          14 2026-10-07 21:26 files/home/bob/public_html/.env
-rw-r--r-- bob/bob          13 2026-10-07 21:26 files/home/bob/public_html/.env.example
-rw-rw---- www-data/www-data 17 2026-10-07 21:26 files/var/www/shop/app/etc/env.php
-rw------- 0/0            2956 2026-10-07 21:26 manifest.json

Restoring from a backup

Restoring needs only age and tar, not ssv. Do it on a machine you trust that has the private key. Avoid keeping decrypted secrets on disk longer than necessary.

  1. List the contents:

    age -d -i ssv-main.key web01-20261007.age | tar -tvf -
    

    For a passphrase backup, leave out -i ssv-main.key; age asks for the passphrase. This applies to every command below.

  2. Check the manifest, in particular its skipped list:

    age -d -i ssv-main.key web01-20261007.age | tar -xOf - manifest.json | less
    
  3. As root, extract into a private staging directory. Running tar as root restores each file's owner, group and permissions:

    sudo install -d -m 700 /root/ssv-restore
    age -d -i ssv-main.key web01-20261007.age | sudo tar -xpf - -C /root/ssv-restore
    

    tar matches owners by user and group name, which is right when restoring to a rebuilt server where the IDs may differ. To restore the original numeric IDs instead, add --numeric-owner.

  4. Copy back only the files you need, keeping their ownership and permissions:

    sudo cp -a /root/ssv-restore/files/home/alice/app/.env /home/alice/app/.env
    
  5. Delete the staging directory, which holds unencrypted secrets:

    sudo rm -rf /root/ssv-restore
    

Last updated 2026-10-08 · for ssv 0.2.0