Backup subsystem overview & architecture
A BOA host carries two generations of off-site backup tooling, all built on
Duplicity and all installed together by
BOA.sh.txt. They differ in config surface, bucket scheme, provider
coverage, and whether they are licence-gated.
The orchestrators
| Tool | Lives at | Targets | Config source | License gate |
|---|---|---|---|---|
multiback |
/opt/local/bin/multiback |
9 providers / 11 backend keys | /root/.remote_backups/ |
_verify_boa_keys (pro or dev) |
mybackup |
/opt/local/bin/mybackup |
same 11 keys, per-tenant | /data/disk/<user>/.../remote_backups/ |
none (tenant front of multiback) |
duobackboa |
/opt/local/bin/duobackboa |
AWS S3 only, own bucket | /root/.duobackboa.cnf (_AWS_*) |
none |
backboa |
/opt/local/bin/backboa |
AWS S3 only | /root/.barracuda.cnf (_AWS_*) |
none |
dcysetup is the installer/wiring tool for the multiback suite; it is also
licence-gated. backboa and duobackboa carry no _verify_boa_keys call,
so they are physically present and runnable on every host regardless of tier
— "LTS-only" is a usage recommendation, not an enforced restriction. The
practical split is: multiback/dcysetup are the modern, licence-gated,
multi-provider path; backboa/duobackboa are the simpler single-bucket
AWS-S3 legacy path.
Supported providers
multiback and mybackup target nine storage providers across the eleven
backend keys enumerated in multiback operations —
Amazon S3 is one provider but carries three storage-class keys (aws,
aws_one_zone, aws_standard_ia), which is how nine providers become eleven
keys. The key whose credential file you fill in under
/root/.remote_backups/credentials/ picks the destination;
Regions & buckets covers the per-key target-URL and
bucket-name mechanics. The two tables below compare the providers on capability
and on cost to inform that choice.
The figures below are a point-in-time snapshot (as of 2026-07) and drift — storage and egress pricing especially. Verify the current numbers against each provider's own pricing and product pages before committing to a backend.
Capability comparison
| Service | Storage class | Redundancy | Regions | Encryption | Interface |
|---|---|---|---|---|---|
| Amazon S3 | Standard, One Zone-IA, Standard-IA | Multi-AZ / Single AZ | Global | Server-side (AES-256) + client-side | S3 API (boto3) |
| Backblaze B2 | Hot | Multi-region | US, Europe | Server-side (AES-256) + client-side | B2 API, S3-compatible |
| Cloudflare R2 | Hot | Multi (region-less) | Global | Server-side (AES-256) + in-transit TLS | S3 API (boto3) |
| DigitalOcean Spaces | Standard (Hot) | Multi-region | Global | Server-side (AES-256) + client-side | S3 API (boto3) |
| Google Cloud Storage | Standard, Nearline, Coldline, Archive | Multi-region | Global | Server-side (AES-256) + client-side | Native, S3-compatible |
| IBM Cloud | Standard, Vault, Cold Vault, Archive | Multi-region | Global | Server-side (AES-256) + client-side | S3 API (boto3) |
| Linode Object Storage | Standard (Hot) | Multi-region | Global | Server-side (AES-256) + client-side | S3 API (boto3) |
| Microsoft Azure | Hot, Cool, Archive | LRS, ZRS, GRS, RA-GRS | Global | Server-side (AES-256) + client-side | Azure Blob API |
| Wasabi | Hot | Multi-region | Global | Server-side (AES-256) + client-side | S3 API (boto3) |
Cost comparison
| Service | Storage cost (per GB) | Egress cost (per GB) | Free tier | Notes |
|---|---|---|---|---|
| Amazon S3 | $0.0230 (Standard) | $0.090 | 5 GB (12 months) | Wide region availability, multiple classes |
| Backblaze B2 | $0.0050 | $0.010 | 10 GB storage + 1 GB/day download | Cost-effective, ideal for archival storage |
| Cloudflare R2 | $0.0150 | Free | 10 GB storage + 1 TB egress/month | Zero egress fees, integrates with their network |
| DigitalOcean Spaces | $0.0200 over 250 GB | $0.020 per GB beyond 1 TB | None | Free bandwidth up to the first 1 TB |
| Google Cloud | $0.0200 (Standard) | $0.120 | 5 GB (12 months) | Multiple storage classes |
| IBM Cloud | $0.0200 (Standard) | $0.090 | Lite plan (25 GB) | Supports advanced archival options |
| Linode Object Storage | $0.0050 | $0.010 | None | Affordable and Akamai-backed |
| Microsoft Azure | $0.0180 (Cool) | $0.085 | $200 credit for first 30 days | Flexible tiering |
| Wasabi | $0.0059 | Free | None | Unlimited egress, good for high traffic |
Why Duplicity
All four tools shell out to /usr/local/bin/duplicity (pinned 3.0.6,
Python 3.13.9 for all four tools — dcysetup install and
backboa install pin the identical versions; built/installed by
dcysetup or backboa install). Duplicity provides:
- Encryption before upload.
multiback/mybackupuse a symmetric passphrase read from.secret.txtintoPASSPHRASE;backboa/duobackboause_AWS_PWDinto the same env var. Some backend paths run--no-encryption(thecustompath-set on hosted systems). - Full + incremental chains.
_set_modepicksfullvsincrementalper run from cache state and whether today's full-log already exists — not by readingFULL_BACKUP_FREQUENCY— never by a CLI verb. The full cadence is enforced separately by Duplicity's--full-if-older-than ${FULL_BACKUP_FREQUENCY}, embedded in the default backup command (_DCY_BUP_CMD). On hosted systems theglobal/datapath-sets instead use_FBF_BUP_CMD, which omits--full-if-older-thanand forces a full only on the first of the month (_DOM=1). - Point-in-time restore via
--time/--path-to-restoreagainst a volsize-300 archive.
The Duplicity invocation runs at --concurrency scaled by core count
(_useCpu = 1/2/4 for up-to-4 / up-to-8 / over-8 CPUs) and --volsize 300.
What multiback actually captures
multiback does not back up a hard-coded path list. It backs up whatever the
selected path-set (<user> argument) resolves to, sourced from a
paths.txt file via _load_paths. The root-side path-sets are generated by
dcysetup setup (create_global_paths_config.sh):
global—_SOURCE="/etc /opt/solr4 /var/aegir /var/solr7 /var/solr9 /var/www /var/xdrago"plus generated include globs for/data/disk/arch,/var/backups/csf,/var/backups/dragon,/var/backups/reports, and an include-regexp for/root/.*.cnf.data— empty_SOURCE; an auto-generated per-/data/disk/<user>include set, which also appends--include /data/all,--include /data/confand--include /home. It excludes each tenant's.tmp,backup-exports,backups,clients,src,u,undo,static/{.tmp,restores,tmp,trash}, each/home/<user>.ftpaccount's own.tmp,backups,clients,platformsandstatic, plus/var/www. The excludes are written as literal paths (/data/disk/<user>/backups,/data/disk/<user>/backup-exports); there is no exclude forstatic/files/.backupsorstatic/files/.backup-exports, so on a relocated account the excluded names are only symlinks while the real tarball stores sit inside the included tree — one more reason to read the generatedpaths.txt.custom— operator-supplied include/exclude lists only.
A per-tenant mybackup run uses /data/disk/<user>/remote_backups/paths/paths.txt.
Because _SOURCE and the include/exclude sets are config-driven, the exact
captured set is host-specific; read the generated paths.txt to know what a
given host backs up, rather than assuming a fixed list.
Per-account backup directories may be symlinks
Ægir's per-site backup tarballs land in /data/disk/<account>/backups
(Provision derives backup_path as <aegir_root>/backups) and backup-download
exports in /data/disk/<account>/backup-exports; the nightly per-account run
re-asserts aegir_backup_export_path to <account>/backup-exports right
after running the relocation check below.
Post-5.10.3, when an account's static/files resolves to a different
device than the account root (stat -c '%d' on the account vs
stat -L -c '%d' on static/files), the nightly per-account run moves both
directories onto the static filesystem and replaces them with symlinks:
/data/disk/<account>/backups -> /data/disk/<account>/static/files/.backups
/data/disk/<account>/backup-exports -> /data/disk/<account>/static/files/.backup-exports
Backups of native-symlinked sites dereference the files/private symlinks,
so the tarballs are large and would otherwise fill the root partition; on a
default single-filesystem box the device check makes this a deliberate no-op.
The Ægir paths keep working through the symlinks, and both stores are
deliberately kept on one filesystem so the backup-download hardlinks between
backups and backup-exports keep working — hardlinks cannot cross
filesystems. The leading-dot store names are skipped by the site/orphan scan.
To check whether an account is relocated:
readlink /data/disk/<account>/backups — empty output means a plain
directory, not relocated. Kill-switches: box-wide
/data/conf/disable_backups_on_static_fs.cnf, per-account
/data/disk/<account>/static/control/no_backups_on_static_fs.info.
The migration mechanics — the incremental rsync --remove-source-files move,
the /run/.boa_backups_relocate.flock serialisation, and the
/run/boa_queue_stop.pid task-queue pause — are owned by
Backups on the static filesystem.
The local-dump layer underneath
Off-site backup archives a host that already has local MySQL dumps in place.
mysql_backup.sh (cron 15 1 daily) writes per-DB dumps to
/data/disk/arch/sql/<host>-<date>/<db>/ — mydumper by default
(--sync-thread-lock-mode=AUTO, forced FTWRL on Percona 5.7), with the
mysql system schema always taken by mysqldump --routines --events.
Those dumps live under /data/disk/arch, which the global path-set
includes — so the off-site copy carries the local dump tree. Local-dump
retention (_DB_BACKUPS_TTL, default 14 days) is independent of off-site
retention. See Database.
Known failure — backup binary permissions
/usr/bin/mysqldump and /usr/bin/rsync must be mode 755 so the
unprivileged aegir/Octopus user can execute them. A transient 700 (or
750) on these binaries leaves them unexecutable for that user and breaks
all Octopus/Ægir per-site backups with mysqldump: Permission denied.
autoupboa restores 755 in normal operation and deliberately sets 700
only on a restricted VM — when /root/.restrict_this_vm.cnf is present
(killall -9 rsync; chmod 700 /usr/bin/rsync /usr/bin/mysqldump). If site
backups fail with a permission error, check these two modes before anything
else. See Troubleshooting.
Concurrency and gating
Every tool refuses to run a second Duplicity in parallel: a positive
pgrep -fc duplicity aborts the run (multiback logs to
/var/log/mybackup_waiting_queue.log and, on _INCIDENT_REPORT=ALL, emails a
waiting report). All tools also honour /root/.pause_heavy_tasks_maint.cnf
(exit 0 immediately) and abort if root disk usage is over 90%.
mysql_backup.sh additionally short-circuits on /root/.proxy.cnf.
Related
- multiback operations — config tree + install.
- Retention —
KEEP_WITHIN/FULL_BACKUP_FREQUENCY. - CLI reference — verbs and argument order.
- Reference appendix — consolidated variable table.