pg_data named volume declared in docker-compose.yml.
That volume outlives container recreation, docker compose up --build,
docker compose down, host reboots, and upgrades of the pgvector/pgvector
image. Two things remove it: docker compose down -v, and losing the machine.
The same command takes caddy_data with it, so a rebuilt instance refetches
its TLS certificates and spends part of the Let’s Encrypt issuance limit for
your domain.
What gets backed up
These records exist only in this database, and a backup is the only way to get them back:- accounts and platform identities
- installations and grants
- registered OAuth clients
- channel write policies
- write limits and rate tiers
- encrypted Slack credentials, tokens issued to your instance specifically
- it re-fetches message history from Discord
- it generates new embeddings for each message chunk
- it reads image attachments again through the vision model
.env.example already warns about for changing MOB_BASE_URL after
launch: your instance is its own
OAuth issuer, so every connected agent presents a grant against an identity
that no longer exists and stops working at the next request. Recovery runs
through the original install path, with an owner reinstalling the Discord
and Slack apps and every member reconnecting their agent and reauthorizing
Slack. Discord and Slack themselves are unaffected.
The backup and the .env are one artifact
slack_installation_credentials and slack_user_authorizations store
ciphertext. The key that reads them is MOB_CREDENTIAL_ENCRYPTION_KEY, which
lives in .env and never enters the database. Restoring a backup next to a
different key produces rows no one can decrypt, and every affected
installation has to reauthorize with Slack before search works again. Back up
.env whenever you back up the database and keep the pair together.
Creating a backup
-T disables TTY allocation, which is what lets the same line run from cron.
Restoring
Restore before the api starts. The api applies any migration file not yet recorded inschema_migrations on boot, so a backup from an older release
lands on a stopped stack and gets brought forward by the next start.
pgvector/pgvector image ships it; a plain postgres image does not.
Test the restore once against a throwaway machine before you need it. None
of these failures is visible until you try:
- a missing pgvector extension
- a role mismatch
- an encryption key that no longer matches the ciphertext
Getting the backup off the machine
A backup stored on the server covers a bad upgrade or a mistaken delete, and a machine failure destroys the backup together with the volume it protects. mob ships no backup service indocker-compose.yml because a container
writing backups beside that volume sits in the same failure domain. Two
arrangements leave that domain:
- point
MOB_DATABASE_URLat a managed Postgres with point-in-time recovery and remove thepostgresservice from the compose file - keep the local database and pipe each backup to object storage with
restic or rclone, storing
.envin the same repository