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

# Backup, upgrade and recovery

> Verify a recoverable backup before changing the deployed version.

Before every upgrade, take a custom-format PostgreSQL dump and encrypt it with
a separately retained age recipient. Keep passwords, dumps and decryption keys
outside Git. From the image bundle directory:

```sh theme={null}
umask 077
docker compose --env-file config.env -f compose.yml exec -T postgres \
  pg_dump -U pup_migration -d pup --format=custom --no-owner --no-acl \
  | age -r "$BACKUP_RECIPIENT" -o backup.dump.age
```

Decrypt and restore into an isolated empty PostgreSQL 18 instance using
`pg_restore --exit-on-error --no-owner --no-acl`. Compare table counts,
migration version, case/warning/receipt identifiers and event/outbox JSON object
types. Read cases from the restored instance. A dump is not a verified backup
until the restore rehearsal succeeds. Never attach the live volume to it.

## Upgrade

Retain the previous signed manifest, four image digests and configuration.
Review schema compatibility and Discord application identity. Replace image
digests using the new verified manifest, run the one-shot migration service,
restart services and verify readiness, Cases and Discord delivery.

## Rollback

Migrations are forward-only. Restore earlier application digests only when they
support the current schema. Otherwise restore into a separate database, verify
it, and deliberately switch connections. Do not delete the live volume or
overwrite immutable release identities during recovery.

Documentation or progress-publication failure is separate from application
health. Retry the same accepted publication without undoing a healthy deployment.
