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

# Back up PostgreSQL

> PostgreSQL is the durable system of record, so it is the only thing that needs backing up. Redis holds reconstructible queue state only.

Back up PostgreSQL and nothing else. Every app, keyword, position, score, review, change event and action item lives there.

Redis holds queue state, which is reconstructible: the next daily run rebuilds it. Including Redis in an application data backup adds size and restores nothing you need.

## Take a backup

The block below writes a compressed custom format archive, fails on any error, and verifies that PostgreSQL can read its table of contents before declaring success.

```bash theme={null}
set -eu
mkdir -p "$HOME/asobeast-backups"
chmod 700 "$HOME/asobeast-backups"
umask 077
backup_file="$HOME/asobeast-backups/asobeast-$(date -u +%Y%m%dT%H%M%SZ).dump"
docker compose exec -T postgres pg_dump --username asobeast --dbname asobeast --format=custom > "$backup_file"
test -s "$backup_file"
docker compose exec -T postgres pg_restore --list < "$backup_file" > /dev/null
echo "$backup_file"
```

`umask 077` gives the archive restrictive permissions, which matters because the dump contains everything.

Run it from the repository root with the same Compose project name the deployment uses. The default project name is the directory name. For a deployment started with `docker compose -p production`, add `-p production` to every command on this page.

## Where does the data live?

In the named volume `pgdata`, mounted at `/var/lib/postgresql` in the `postgres` container. Compose prefixes it with the project name, so it appears as `production_pgdata` under a project called `production`.

Copying the volume directory while PostgreSQL is running is not a backup. Use `pg_dump`, which produces a consistent snapshot.

## Automate it

Put the block in a script and schedule it, keeping several generations.

```bash theme={null}
0 2 * * * /usr/local/bin/asobeast-backup.sh >> /var/log/asobeast-backup.log 2>&1
```

Then prune old archives on whatever window your storage allows.

```bash theme={null}
find "$HOME/asobeast-backups" -name 'asobeast-*.dump' -mtime +30 -delete
```

Keep tested generations off host. A backup on the same disk as the database survives a mistake but not a disk.

<Warning>
  Never commit a database dump or a `.env` file to version control. Both contain credentials, and a dump contains every account record.
</Warning>

## Verify a backup

A backup that has never been restored is not a backup. Schedule a real restore into a disposable stack on a regular cadence and confirm the app and keyword counts match.

See [Restore PostgreSQL](/operations/restore).

## Related

<CardGroup cols={2}>
  <Card title="Restore PostgreSQL" icon="database" href="/operations/restore">
    The other half of the drill.
  </Card>

  <Card title="Upgrade and roll back" icon="circle-arrow-up" href="/install/upgrade">
    Why the backup has to precede the upgrade.
  </Card>
</CardGroup>
