Zorruno Docker Backup Scripts
Modular backup scripts for Docker Compose servers such as Panda and Beaver.
Each server has its own untracked .env and containers.txt, while all script
logic remains common and can be updated through Git.
Pipeline
backup.sh runs these stages in order:
stages/10-containers.sh— stop, archive, pull, start and verify projects.methods/20-rclone.sh— copy archives to an rclone destination when enabled.methods/30-smb.sh— copy archives to a mounted SMB share when enabled.stages/90-prune.sh— retain the newest configured number locally.
Every stage can also be run directly. The old docker-backup.sh name invokes
the container archive stage, while dockerbackup_prune.sh invokes the local
prune stage. Use the new backup.sh for the complete pipeline.
Requirements
- Bash 4 or later
- Docker with
docker compose, or legacydocker-compose - GNU
tarwith zstd support flockrcloneonly when the rclone method is enabledrsyncandmountpointonly when the SMB method is enabled
The account running the scripts must be allowed to control Docker and read all
files under /dockervolumes.
Installation on Beaver
sudo git clone https://home.fox.co.nz/gitea/zorruno/zorruno-backupdockerscript.git /backupdockerscripts
cd /backupdockerscripts
sudo cp .env.example .env
sudo cp containers.txt containers.local.txt
sudo chmod 600 .env
sudo chmod +x backup.sh docker-backup.sh dockerbackup_prune.sh stages/*.sh methods/*.sh
Edit .env. Beaver's Docker source is already represented by:
SOURCE_BASE_DIR="/dockervolumes"
BACKUP_HOSTNAME="beaver"
Choose and create a local staging path for BACKUP_ROOT. /dockerbackups is
the default, but it should be on a filesystem with enough free space to hold
at least KEEP_COUNT copies of every listed project.
Edit Beaver's untracked containers.local.txt. Each active line can be either a running
container name or a directory name beneath /dockervolumes. Blank lines and
text after # are ignored. If several entries resolve to the same Compose
project directory, that project is backed up only once. Keeping this host-local
file out of Git prevents a pull on Panda or Beaver from replacing its list.
Configuration
Copy .env.example to .env; .env is ignored by Git. Important values:
| Variable | Purpose |
|---|---|
SOURCE_BASE_DIR |
Parent of Compose project directories |
BACKUP_ROOT |
Local archive staging and retention tree |
CONTAINERS_FILE |
Absolute path, or path relative to this repository |
BACKUP_HOSTNAME |
Host folder/name embedded in archive filenames |
BACKUP_METHODS |
Ordered method names, default rclone smb |
ZSTD_EXTRA_OPTS |
Options passed to zstd, default -T0 -12 |
KEEP_COUNT |
Local archives retained per Compose project |
PULL_IMAGES |
Pull images before restarting (1 or 0) |
Rclone
RCLONE_ENABLED=1
RCLONE_REMOTE="gdrive:dockerbackups"
Archives are copied beneath <remote>/<BACKUP_HOSTNAME>/. The method uses
rclone copy, so it does not delete older destination files. Set
RCLONE_HOST_SUBDIR=0 when RCLONE_REMOTE already names the final host-specific
destination; otherwise the host name is appended automatically.
SMB
Mount the SMB share through /etc/fstab or a systemd mount unit; do not put SMB
credentials in this repository. Then configure:
SMB_ENABLED=1
SMB_MOUNT="/mnt/dockerbackups"
SMB_HOST_SUBDIR=1
The script refuses to copy unless SMB_MOUNT is an actual mount point. This
prevents a failed network mount from silently filling the server's root disk.
Running
Run the complete pipeline:
sudo /backupdockerscripts/backup.sh
Preview all stages without stopping containers, copying, or deleting:
sudo /backupdockerscripts/backup.sh --dry-run
Run individual parts:
sudo stages/10-containers.sh --dry-run
sudo methods/20-rclone.sh --dry-run --force
sudo methods/30-smb.sh --dry-run --force
sudo stages/90-prune.sh --dry-run
--force runs a transfer method even when its *_ENABLED value is false. It
still requires the method's destination to be configured and available.
Adding another backup method
Add an executable script named methods/NN-name.sh, for example
methods/40-restic.sh. It should load lib/common.sh, accept --dry-run, and
return a non-zero status on failure. Add its short name to .env:
BACKUP_METHODS="rclone smb restic"
The main pipeline discovers the numbered file from the short name and runs it after the earlier methods. A failed method stops the pipeline, so pruning will not run after a failed transfer.
Archive and restore format
Archives are stored as:
<BACKUP_ROOT>/<project>/<host>.<project>.<date_time>.tar.zst
Each archive contains the project's top-level directory rather than absolute paths. Example restore into an empty temporary directory:
mkdir -p /tmp/docker-restore
tar --zstd -xf backup.tar.zst -C /tmp/docker-restore
Inspect and verify restored content before replacing a live project.