7.5 KiB
Backup helper services
This directory holds the lightweight services that create and restore the stack backups. They are kept separate from the main docker-compose.yml so you can start them on demand.
Files
backup.yml– defines the scheduled backup sidecars:pgbackupdumps the configured Postgres databases into./backups/databases.backup-managerwaits for the latest DB dump, archives mounted application volumes, and rotates dated backup folders in./backups.
restore.yml– provides a one-shotrestorehelper container that mounts the same named volumes and the./backupsdirectory for restores.../scripts/backup/*.sh– shell scripts that power the services:pg-dump.sh,manage-backups.sh,restore.sh– generic helpers used by the containers.restore-odoo.sh,restore-gitea.sh,restore-opencloud.sh– wrappers that restore their respective volumes and (for Odoo/Gitea) the matching Postgres database dump in one shot.
Configuring backups (backup.yml)
Environment variables (usually sourced from .env) let you control what gets dumped and how long backups are kept:
| Variable | Used by | Purpose |
|---|---|---|
POSTGRES_ADMIN_USER, POSTGRES_ADMIN_PASSWORD |
pgbackup |
Credentials for the Postgres superuser/operator running pg_dump. |
BKP_DB_LIST |
pgbackup |
Space-separated list of database names to dump each run. |
BKP_RETENTION_DAYS |
backup-manager |
Delete dated backup directories and SQL dumps older than this many days (default 7). |
BKP_RETENTION_COUNT |
backup-manager |
Keep only the newest N dated directories when >0 (directories newer than the day threshold are kept regardless). |
backup-manager mounts the live volumes (odoo-filestore, odoo-config, gitea_data, etc.) read-only so it can archive their contents into timestamped tarballs. It also moves the SQL dumps from ./backups/databases into each dated directory after the dump job finishes. The script writes a marker file (.last_backup_complete) so the manager only starts once the database dump completes; adjust DB_BACKUP_TIMEOUT in the environment if you want to change the wait time (default 600 s).
To start the scheduled backup helpers alongside the stack:
docker compose -f docker-compose.yml -f backup/backup.yml up -d pgbackup backup-manager
Restoring backups (restore.yml)
The restore helper uses the same Postgres image so it already ships with psql, tar, and gzip. Run the helper with your main compose file so the volumes resolve correctly:
# Open a shell inside the helper
docker compose -f docker-compose.yml -f backup/restore.yml run --rm restore sh
Or call the bundled restore script directly via the helper entrypoint:
# List dated backup directories
docker compose -f docker-compose.yml -f backup/restore.yml run --rm restore list
# Restore the Odoo filestore archive into the named volume (services should be stopped first)
docker compose -f docker-compose.yml -f backup/restore.yml run --rm restore \
restore-volume odoo-filestore /backups/2025-11-13/odoo_filestore_2025-11-13.tar.gz
# Restore a database dump (uses POSTGRES_HOST env, defaults to `postgres`)
docker compose -f docker-compose.yml -f backup/restore.yml run --rm restore \
restore-db /backups/2025-11-13/odoodb_2025-11-13.sql odoodb postgres "$POSTGRES_ADMIN_PASSWORD"
Shortcut restore scripts
For the most common restore scenarios, use the convenience wrappers in scripts/backup/:
restore-odoo.sh BACKUP_ID [DB_NAME] [DB_USER]– stops theodooservice, restores the filestore/config/addons/web/logs/db-data archives and, whenODOO_DB_PASSWORDis present, replays thebackups/<BACKUP_ID>/<db>_<BACKUP_ID>.sql(.gz)dump that was moved into the same dated folder. Defaults: DB name fromODOO_DB/ODOO_DB_NAME, user fromODOO_DB_USER, fallbacksodoo/odoouser.restore-gitea.sh BACKUP_ID [DB_NAME] [DB_USER]– stops thegiteaservice, restores the data archive and, whenGITEA_DB_PASSWORDis available, thebackups/<BACKUP_ID>/<db>_<BACKUP_ID>.sql(.gz)dump residing alongside the volume archives. Defaults: DB name/user fromGITEA_DB/GITEA_DB_NAMEandGITEA_DB_USER, fallbackgitea.restore-opencloud.sh BACKUP_ID– stops theopencloudservice and restores the OpenCloud data archive before starting the service again.
All three scripts automatically source the repository .env (if present) for credentials, run from the repository root, and restart the stopped service once extraction finishes. If a matching SQL dump is found but the corresponding password variable is not set the script skips the database restore with a warning so you can retry after exporting the secret. They expect docker compose to be on the PATH and the helper restore service available.
The restore helper now defaults to the service database user/password for connectivity (so values like ODOO_DB_USER, ODOO_DB_PASSWORD, ODOO_DB_HOST, etc. are enough for most restores). By default it drops the existing database before replaying the dump (DROP_EXISTING_DB=1, overridable per service via ODOO_DROP_EXISTING_DB / GITEA_DROP_EXISTING_DB). Set the flag to 0 if you prefer to manage cleanup manually. If the configured user lacks the privileges to drop/create databases you can export POSTGRES_ADMIN_USER, POSTGRES_ADMIN_PASSWORD, and optionally POSTGRES_ADMIN_DB to elevate the operation; otherwise prepare the database yourself before rerunning the script.
Example:
# Restore Odoo from a dated backup directory and replay its DB dump
./scripts/backup/restore-odoo.sh 2025-11-13_04-15
# Restore Gitea using custom database credentials
GITEA_DB_PASSWORD=supersecret ./scripts/backup/restore-gitea.sh 2025-11-13_04-15 giteadb gitea
# Restore OpenCloud files only
./scripts/backup/restore-opencloud.sh 2025-11-13_04-15
Under the hood the helper mounts:
./backups(read-only) so you can access tarballs and SQL dumps.- The named volumes (
odoo-filestore,odoo-config,odoo-addons,gitea_data,opencloud-data, etc.) so extraction happens directly into the running stack’s storage. restore.sh, which provides thelist,restore-volume, andrestore-dbcommands (runrestore helpfor usage inside the container).
Best practices
- Stop (or put into maintenance) any service that uses a target volume or database before restoring to avoid live writes fighting the restore.
- After a restore, restart the services you paused and verify permissions inside the volume; you might need to run a
chownif the target container expects a different UID/GID. - Test your backup/restore workflow in a safe environment so you know the round-trip works before you need it in production.
- Keep an eye on
/var/log/backup.loginside thepgbackupandbackup-managercontainers if you need to confirm when jobs run or troubleshoot failures.
Troubleshooting
- Backups didn’t rotate: confirm
BKP_RETENTION_DAYS/BKP_RETENTION_COUNTare exported and that cron has run long enough for the age threshold to be exceeded. - Manager starts too early: raise
DB_BACKUP_TIMEOUTor make sure the database list inBKP_DB_LISTonly contains existing databases. - Restore fails with permission errors: ensure the target volume is mounted read-write for the restore step, or restore into a temporary directory and adjust ownership manually.
If you need frequently used restore commands, consider adding shell aliases or Makefile targets (e.g., make restore-odoo DATE=2025-11-13).