Back up and restore
Two things hold everything: the data directory, and pds.env. Lose the
directory and the accounts are gone. Lose pds.env and you lose the
rotation key, which is the control of every account’s DID, and the JWT
secret, which signs every session.
The data directory
Section titled “The data directory”| Path | Content | Keep |
|---|---|---|
account.sqlite |
accounts, passwords, sessions, invites, OAuth state, credential recovery epochs | yes |
sequencer.sqlite |
the event log the firehose serves | yes |
did_cache.sqlite |
cached DID documents | no, rebuilt |
audit.sqlite |
the audit log | yes |
ops.sqlite |
commits in flight, operator roles, blocked domains | yes |
actors/<xx>/<did>/store.sqlite |
an account’s repository, records and blob index | yes |
actors/<xx>/<did>/key |
the account’s signing key | yes |
actors/reserved_keys/ |
signing keys of sign-ups in progress | no |
blocks/<did>/<cid> |
blobs | yes |
tempt/, quarantine/ |
uploads in progress; blobs of taken-down accounts | quarantine, yes |
linkjar-pds.lock |
held by the running server | no |
Each SQLite file has a -wal and a -shm sibling while the server runs.
A copy made while the server runs must take them too, or it may miss the
last writes. A cold backup avoids the question.
Keep the whole account database, including its credential_epoch table.
That table prevents tokens issued before password recovery from working
again. A restore of an older backup also restores its older credentials,
sessions and recovery state. If those credentials were compromised,
repeat recovery before reopening the server. On
Postgres, the table is part of the account schema.
A cold backup
Section titled “A cold backup”Stop the server, copy, start. The pause is a few seconds; Caddy keeps answering, with a 502, meanwhile.
docker compose stop pdssudo tar -czf ../pds-backup-$(date +%F).tar.gz data pds.env .envdocker compose start pdsMove the archive off the host. It holds the secrets of pds.env, so
treat it as a secret.
Continuous replication
Section titled “Continuous replication”LinkJar’s own hosts replicate the SQLite files with
Litestream to object storage and sync the blobs
beside them. With Litestream in charge of checkpoints, set
PDS_SQLITE_DISABLE_WAL_AUTO_CHECKPOINT=true. Rehearse the restore before
you rely on it.
One account
Section titled “One account”export-car writes an account’s repository as a CAR file, the format
every AT Protocol tool reads and the one an account takes to another
server:
docker compose exec -T pds linkjar-pds export-car did:plc:… > alice.carBlobs are not in the CAR; they are the files under blocks/<did>/.
Restore
Section titled “Restore”On a fresh host with the Compose files in place:
-
Unpack the archive where the Compose file is, and give the data directory to the server’s user:
Terminal window sudo chown -R 1000:1000 data -
Check the directory.
verifyreads every account, compares each repository with its records and blobs, and prints a summary that ends withdifferences 0:Terminal window docker compose run --rm pds linkjar-pds verify -
Start the server, point DNS at the new host, and check it from outside.
The relay resumes at its cursor. A restore from a backup older than the last events the relay saw leaves the relay ahead of the server; the relay then re-crawls the repositories at their next commit.
