For AI agents: a documentation index is available at /llms.txt. A markdown version of this page is available at /guide/community/operate/backup-and-restore.md.
Backup and restore
What to back up, how to automate it, and how to prove a restore actually works.
A Community install holds two things you cannot regenerate: the PostgreSQL database and the attachment files. Back up both, or your restore will be half a system.
An untested backup is not a backup
Restore into a scratch instance at least once. Most backup failures are discovered during the first real restore, which is the worst time.
#What to back up
| Item | Docker location | Manual location |
|---|---|---|
| Database | orangescrum-postgres-data volume | Your PostgreSQL data directory |
| Attachments | orangescrum-app-files volume | webroot/files |
| Configuration | orangescrum-app-config volume | config/ |
The config volume is small but holds the generated database configuration โ including it saves a fiddly manual step on restore.
#Backing up (Docker)
#!/usr/bin/env bash
set -euo pipefail
STAMP=$(date +%F-%H%M)
DEST=/backups/orangescrum
mkdir -p "$DEST"
# Database โ custom format compresses and restores selectively.
docker compose exec -T orangescrum-postgres \
pg_dump -U orangescrum -Fc orangescrum > "$DEST/db-$STAMP.dump"
# Attachments.
docker run --rm \
-v orangescrum_orangescrum-app-files:/files:ro \
-v "$DEST":/backup alpine \
tar czf "/backup/files-$STAMP.tar.gz" -C /files .
# Configuration.
docker run --rm \
-v orangescrum_orangescrum-app-config:/config:ro \
-v "$DEST":/backup alpine \
tar czf "/backup/config-$STAMP.tar.gz" -C /config .
# Keep 30 days.
find "$DEST" -type f -mtime +30 -delete
Check your volume prefix
Compose prefixes volume names with the project name โ orangescrum-oss in the
shipped file. Confirm with docker volume ls and adjust the script.
Schedule it nightly:
0 2 * * * /usr/local/bin/orangescrum-backup.sh >> /var/log/orangescrum-backup.log 2>&1
#Backing up (manual install)
pg_dump -U orangescrum -Fc orangescrum > db-$(date +%F).dump
tar czf files-$(date +%F).tar.gz -C /var/www/orangescrum/webroot files
tar czf config-$(date +%F).tar.gz -C /var/www/orangescrum config
#Restoring
Stop the application
docker compose stop orangescrum-appRestoring underneath a running app produces inconsistent state.
Restore the database
docker compose exec -T orangescrum-postgres \ dropdb -U orangescrum --if-exists orangescrum docker compose exec -T orangescrum-postgres \ createdb -U orangescrum orangescrum docker compose exec -T orangescrum-postgres \ pg_restore -U orangescrum -d orangescrum < /backups/orangescrum/db-2026-08-01-0200.dumpRestore attachments
docker run --rm \ -v orangescrum_orangescrum-app-files:/files \ -v /backups/orangescrum:/backup alpine \ sh -c "rm -rf /files/* && tar xzf /backup/files-2026-08-01-0200.tar.gz -C /files"Start and clear caches
docker compose start orangescrum-app docker compose exec orangescrum-app php bin/cake.php cache clear_allVerify
Sign in, open a project, open a task with an attachment, and download it. A database-only restore looks fine until someone clicks a file.
#Testing the restore
Quarterly, restore into a throwaway instance on a different port:
docker compose -p orangescrum-test up -d
Then run the restore against that project name. It costs an hour and tells you whether your backups are real.
#What to watch
A sudden drop usually means the dump failed and wrote a near-empty file. Alert on it.
This is what fills the disk. Track it before it stops the app.
A backup on the same host does not survive losing the host.
Dumps contain everything. Encrypt them at rest.
Common failures and how to read the logs.