Backup and Restore

A Pengin-Pi-3 project is four things, and a backup plan has to cover all four:

  1. The database — users, pages, wiki content, roles, history, and dynamic content
  2. Media — uploaded images, video, and files, if you store them locally
  3. Your code — every app, template, and change you've made on top of the core
  4. Your keys — the .env file

Lose the database and you lose the site's content. Lose the code and you lose your work. Lose the keys and you're rebuilding access to everything else. None of them can be recovered from the others.


The minimum

If you do nothing else on this page, do this:

  • Export the database regularly and keep the copy on a separate backup drive
  • Add a git remote on that backup drive and push your code to it
  • Keep a copy of .env somewhere secure that you can find again

That's enough to recover from a dead laptop, a stolen machine, or a failed drive. Everything below is about doing it better.


Backing up the database

Relational data and FerretDB documents live in one PostgreSQL instance, but in two databases:

  • DB_NAME — Django's tables: users, slugs, wiki pages, roles, history
  • FERRETDB_DB (default postgres) — FerretDB's document storage, which holds dynamic content-type data

Dumping only DB_NAME silently misses every dynamic content entry. Back up the whole instance.

Under Docker

docker compose exec -T postgres pg_dumpall -U "$DB_USER" \
  > backup-$(date +%F).sql

-T disables the TTY, which you need when redirecting output or running from cron.

Compress it — SQL dumps shrink dramatically:

docker compose exec -T postgres pg_dumpall -U "$DB_USER" \
  | gzip > backup-$(date +%F).sql.gz

Without Docker

pg_dumpall -h 127.0.0.1 -U penginpi > backup-$(date +%F).sql

A portable copy of documents

A pg_dumpall restores cleanly onto the same PostgreSQL and DocumentDB versions. For a copy of the document data that doesn't depend on those versions, export it through FerretDB as well:

mongodump --uri "mongodb://$DB_USER:$DB_PASSWORD@127.0.0.1:27017/$DB_NAME" \
  --out documents-$(date +%F)

Worth doing before any FerretDB or DocumentDB version upgrade.

SQLite

A development instance running on the SQLite fallback is one file: db.sqlite3 in the project root. Copy it while the server is stopped.

Before every upgrade

Take a fresh dump before any git pull that includes migrations. The web container applies migrations automatically on start, and Django migrations aren't reliably reversible. The dump is your undo.


Backing up media

Only needed if you store uploads locally. With FILE_STORAGE_BACKEND=s3, uploads live in your S3 bucket and are protected by it — enable versioning on the bucket if you want deleted files to be recoverable.

With local storage, uploads are in ./media in the project root. Copy it to the backup drive.

Linux, macOS, WSL

rsync -av /opt/pengin-pi-3/media/ /mnt/backup/pengin-pi-3/media/

The trailing slashes matter: they copy the contents of media into the destination rather than nesting a second media folder inside it.

Leave out --delete for backups. With it, a file deleted by accident on the server is deleted from the backup on the next run too.

Windows

robocopy C:\pengin-pi-3\media E:\backup\pengin-pi-3\media /E

/E includes subdirectories. Avoid /MIR for backups for the same reason as --delete — it mirrors deletions.

Media is never pruned by the application, so this directory only grows. See Configuration.


Backing up your code

The public mirror has the core. It doesn't have your work — your apps, templates, settings changes, and fixes exist only where you put them.

Version your project in git, and push it somewhere that isn't your working machine.

A remote on your backup drive

A git remote can be a plain directory. Create a bare repository on the backup drive:

git init --bare /mnt/backup/git/my-project.git

Add it as a remote and push:

cd ~/projects/my-project
git remote add

Pages Here

No sub-pages yet.

Page Info

Wiki: Docs

Created on Sep 21, 2026 by Stuart Anderson

Maintainers

Editor Last Activity
Stuart Anderson creator Sep 21, 2026