Documentation

Backup and restore

Dump the one database that matters on a schedule, and restore it into a fresh instance.

This is a self-hosted, single-tenant instance: there is no managed backup behind it. Two things matter, and a backup needs both:

  • the Postgres database (the postgres_data volume): every finding, plan, score and setting;
  • the pyxa_secrets volume: the Django secret key and the ENCRYPTION_KEY that encrypts connector credentials. A database restored without the key it was written with keeps its data, but no stored credential can be read: every connector fails to sign in until each credential is entered again, and the worker log says so.

Redis is a cache and the Celery broker only, and holds nothing that is unsafe to lose.

Back up, on a schedule (cron, a systemd timer), to somewhere other than the PYXA host. The SQL dump is portable across Postgres versions and architectures, unlike a copy of the raw volume:

docker compose exec -T db pg_dump -U "${DB_USER:-patchwork}" "${DB_NAME:-patchwork}" | gzip > "pyxa-$(date +%Y%m%d).sql.gz"
docker run --rm -v pyxa_secrets:/secrets:ro -v "$PWD":/backup alpine tar czf "/backup/pyxa-secrets-$(date +%Y%m%d).tar.gz" -C /secrets .

The secrets archive is as sensitive as the credentials it unlocks: store it encrypted, and apart from the database dump.

Restore into an empty database, then start the application. Loading a dump into a database migrate has already filled fails on every table that exists, and the rows it seeds (content types, permissions) keep their own ids, so permissions end up attached to the wrong models:

docker compose stop django celery-worker celery-worker-bulk celery-beat
docker run --rm -v pyxa_secrets:/secrets -v "$PWD":/backup alpine sh -c "cd /secrets && tar xzf /backup/pyxa-secrets-YYYYMMDD.tar.gz"
docker compose exec -T db dropdb -U "${DB_USER:-patchwork}" "${DB_NAME:-patchwork}"
docker compose exec -T db createdb -U "${DB_USER:-patchwork}" "${DB_NAME:-patchwork}"
gunzip -c pyxa-YYYYMMDD.sql.gz | docker compose exec -T db psql -q -U "${DB_USER:-patchwork}" "${DB_NAME:-patchwork}"
docker compose up -d

The django container migrates on start, so a backup taken by an earlier release is brought up to date by the restore. A backup older than the single initial migration has to be restored with the release it came from first; migrate says which.

File-import connectors (CSV, SARIF, ASFF, BloodHound and the others) read files from the folder mounted as CSV_IMPORT_DIR; back that folder up too, since only what was parsed out of the files is in the database.

Generated from README.md in the PYXA repository.

Make security everyone's job, starting this week.

Run the free edition on your own server today, or see PYXA on your own scanner data in a 30-minute demo.