Dockup reads dockup.* labels from two places:
- named volumes — the volume is the source, so its policy is flat;
- containers — a container may expose several host paths, so bind sources are grouped by a name, and container-level labels decide whether the container may be stopped or has hooks.
Nothing is backed up unless it opts in. Labels are read on every Docker event and on a periodic resync, so changes to containers are picked up live. Named volume labels are immutable in Docker: recreate the volume to change them.
Named volumes
volumes:
appdata:
labels:
dockup.enable: "true"
dockup.schedule: daily
dockup.target: nas,offsite
dockup.keep-daily: "7"
dockup.keep-monthly: "6"
dockup.encrypt: "true"| Label | Type | Default | Description |
|---|---|---|---|
dockup.enable | boolean | false | Opt in. Without it the volume is ignored. |
dockup.schedule | string | defaults.schedule | A name from schedules, or an inline cron expression (0 3 * * *, @daily, @every 6h). |
dockup.target | list | defaults.targets | Comma separated target names. Unknown names are an error. |
dockup.filename | string | {source}_{timestamp} | Archive base name. Placeholders: {source}, {volume}, {bind}, {host}, {schedule}, {timestamp}. |
dockup.compression | enum | zstd | none, gzip, zstd. |
dockup.encrypt | boolean | defaults.encrypt | Encrypt with encryption.age.recipients. |
dockup.verify | boolean | defaults.verify | Re-read the uploaded archive and compare its checksum. |
dockup.catch-up | boolean | defaults.catch-up | Run a backup at startup when the last successful run is older than the upcoming interval. |
dockup.keep-last | integer | defaults.keep-last | Keep the newest N archives. |
dockup.keep-daily | integer | defaults.keep-daily | Keep the newest archive of each of the newest N days. |
dockup.keep-weekly | integer | defaults.keep-weekly | Same, per ISO week. |
dockup.keep-monthly | integer | defaults.keep-monthly | Same, per calendar month. |
dockup.max-age | duration | defaults.max-age | Hard cap (7d, 36h, 4w). The newest archive is never pruned. |
dockup.exclude | list | — | Relative paths or globs to skip, e.g. cache,*.log. |
Retention is disabled when every keep value and max-age are zero: Dockup then never deletes anything.
dockup.stop, dockup.hook and dockup.bind.* are container-only; using them on a volume is reported as a warning.
Containers: bind mounts
A container declares each host path it exposes with dockup.bind.<name>, then configures it with dockup.<name>.<option> using the same options as volumes:
services:
app:
labels:
# sources
dockup.bind.data: /srv/app/data
dockup.bind.config: /srv/app/config
# per-source policy (optional)
dockup.data.schedule: hourly
dockup.data.target: offsite
dockup.data.keep-last: "48"
# container-level defaults for every bind source above
dockup.schedule: daily
dockup.target: nas
# quiescing
dockup.stop: "true"
dockup.hook: postgres| Label | Description |
|---|---|
dockup.bind.<name> | Absolute host path to archive. Required to declare a source. |
dockup.<name>.<option> | Any option from the volume table (schedule, target, filename, compression, encrypt, verify, keep-*, max-age, exclude), plus enabled. |
dockup.<option> | Container-level default for all bind sources of this container. |
dockup.stop | true allows Dockup to stop this container while its sources are archived, then start it again. |
dockup.hook | Name of a hook set from hooks in the configuration. Commands run inside this container. |
Notes:
- Bind source names must not be
stop,hookorbind. - The archive identity of a bind source is its host path, not the source name, so recreating a container keeps its archive history and retention.
- When two containers declare the same path with different policies, Dockup keeps the first (sorted by container name), merges the containers for quiescing and reports a warning.
/is rejected: refusing to archive the host root is a feature.
Quiescing
A backup of a live database can capture a half-written state. Dockup only touches containers that opt in:
- Hooks first — containers with
dockup.hook=<name>run the hook set'sprecommands (sh -c) before anything is stopped, so apg_dumpcan write into the volume that is about to be archived. - Then stop — containers with
dockup.stop=truethat mount (volume sources) or declare (bind sources) the source are stopped, and started again once the archive is finished (or has failed).stop-timeout(default 30s) is the grace period before Docker kills them. - Then archive, and finally run the
postcommands — also when the archive failed, so temporary dumps are always cleaned up.
If a pre command fails, nothing is stopped and the run is recorded as failed.
A container without either label is never touched, even if it mounts the volume.
Naming and layout
Archives are stored per source and schedule:
<target prefix>/<host>/volume/<volume>/<schedule>/<filename>.tar.zst[.age]
<target prefix>/<host>/bind/<host path>/<schedule>/<filename>.tar.zst[.age]The default prefix is dockup, the host defaults to the container hostname. Every archive name carries a UTC timestamp, which is what retention uses to bucket archives: Dockup appends _<timestamp> unless the filename template already contains {timestamp}.