All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
.woodpecker.yml:prepare-tagsnow writes newline-separated tags to.tagsinstead of a single comma-separated line.woodpecker-ci/plugin-docker-buildx'stags_filesplits strictly on\n(unlikethegeeklab/drone-docker-buildx, which needed the opposite — a single comma-separated line, per the.drone.ymlfix below from 2026-06-27). The comma-separated form was read as one literal tag (latest,2.1.6-1.4.4), which Docker rejected as an invalid reference.
- CI migrated from Drone to Woodpecker:
.drone.ymlreplaced by.woodpecker.yml. Same multi-arch (linux/amd64,linux/arm64) build/push tomodem7/borgmatic-docker, Docker Hub README sync, and dynamically-generated tags (latest,<borgmatic_ver>-<borg_ver>fromrequirements.txt), now viawoodpeckerci/plugin-docker-buildxand ahadolintlint step. Also now builds on every push tomaster(previously the Drone pipeline only ran on acustomtrigger event), plus Slack build notifications on success/failure. - README build-status badge now points at Woodpecker instead of Drone.
- KeePassXC credential support via opt-in init script (
data/borgscripts/init-keepassxc-cli.sh). Installs apykeepass-backed shim at/usr/local/bin/keepassxc-clithat implements the exact CLI interface borgmatic calls — no image bloat (~2 MB vs ~150 MB for the Alpine Qt6 package). Seedata/borgscripts/KEEPASSXC-CLI.mdfor setup.
borgmaticis now a thin wrapper aroundborgmatic.bin(the pip-installed binary), allowing the entrypoint to be intercepted cleanly without any behaviour change for users.
- Reorder Dockerfile layers for better cache utilisation:
apkpackages now install before S6 overlay is added, and S6 is extracted in its ownRUNstep. Previously a S6 version bump invalidated the entire apk + pip chain; now each concern caches independently. - Use
--mount=type=cache,id=apk-${TARGETARCH}for theapkstep (consistent with the existing pip cache mount).
renovate.json: addversioningTemplate: "loose"to the S6 overlay custom manager. S6 uses 4-part version numbers (3.2.0.2) which aren't valid semver — withoutlooseversioning Renovate silently skips S6 updates.
- S6 overlay updated from
3.2.0.2to3.2.3.0.
renovate.json: constrainPYTHON_VERSIONupdates to major.minor only (/^\d+\.\d+$/), same asALPINE_VERSION. Prevents Renovate proposing pre-release or Windows-variant tags like3.15.0b3-windowsservercore-ltsc2025which don't exist as valid Alpine-based Python images.
.drone.yml: Drone tags file now written as a single comma-separated line (latest,2.1.6-1.4.4) rather than one tag per line.thegeeklab/drone-docker-buildxtreated the entire file content as one string, causing an invalid tag error (latest\n2.1.6-1.4.4).
data/borgscripts/docker-stop.sh: stops containers carrying a configurable label (backupby default) before a borgmatic run. Configurable label key and stop timeout. RequiresDOCKERCLI=trueanddocker.sockmounted.data/borgscripts/docker-start.sh: starts a single compose stack after a borgmatic run usingdocker compose start(notup -d, so no unintended recreates). Configurable compose project directory. RequiresDOCKERCLI=trueanddocker.sockmounted.data/borgscripts/redis-backup.sh: triggersBGSAVEon a Redis container and polls until the save completes before borgmatic runs. Configurable container name and timeout. The resultingdump.rdbis then included in borgmatic's normal source directory sweep.- README: new "Example borgscripts" section covering all three scripts — configuration variables, label setup, borgmatic wiring, Docker requirements, and a note on AOF mode. New "Native database backup" section covering borgmatic's built-in PostgreSQL, MariaDB, MongoDB, and SQLite support.
- End-to-end CI tests that run borgmatic against a real Borg repository: full backup/restore cycle (repo-create → create → list → extract → verify file contents),
${VAR}env var expansion in borgmatic config, andencryption_passcommandvia a mounted secrets file. - CI test that
borgmatic config validaterejects invalid config with a non-zero exit code.
base-fullbuild/directory removed — all contents moved to repo root (Dockerfile,requirements.txt,.env.template,data/,root/) to match upstream structure. All CI, Drone, Renovate, and README references updated.sync-drone-tags.ymlGitHub Actions workflow removed — redundant since.drone.ymlnow generates tags dynamically fromrequirements.txtat build time.- Drone lint step removed — Hadolint and ShellCheck already run in GitHub Actions CI on every PR.
init-envfileS6 oneshot service — processesFILE__VARNAMEenvironment variables (LinuxServer.io convention) by reading the referenced file and writing its content into the S6 container environment before any other service starts. Complements the existingBORG_PASSPHRASE_FILE/*_FILEexpansion. Dependency chain is now:init-envfile → init-custom-packages → init-custom-scripts → init-config-end → svc-cron.- Renovate tracking for
ALPINE_VERSIONandPYTHON_VERSION— base image versions extracted asARGs so Renovate can open PRs for each independently. - Log rotation example (
logging: driver: local) added todocker-compose.ymlcomments. .gitignoreexcluding.claudedirectory.
- Base image updated from
python:3.14-alpine3.23topython:3.14-alpine3.24. S6_CMD_WAIT_FOR_SERVICES_MAXTIMEreverted to0(unlimited) — 30s could abort startup whenEXTRA_PKGSinstalls large packages on a slow network.docker-compose.restore.yml—/RestoreMountrenamed to/mnt/restore(consistent with main compose and README);BORG_PASSPHRASEadded (required to decrypt repo when mounting archives); shell changed from/bin/shto/bin/bash.VOLUME_RESTORErenamed toBORG_RESTOREin restore compose and.env.template(consistent withBORG_*naming convention).docker-compose.yml— expanded commented examples coveringBORG_SOURCE_*,BORG_REPO,BORG_HEALTHCHECK_URL,CRON/CRON_COMMAND,EXTRA_CRONmulti-line syntax, borgscripts volume, docker.sock, andcustom-cont-init.d.- Borgmatic config examples (
config.yaml,config.yaml.example,config.full.yaml.example) updated to borgmatic 2.x format — deprecatedlocation:,storage:,retention:,consistency:sections removed; all keys promoted to top level;repositories:now uses{path:, label:}objects;checks:uses{name:, frequency:}objects. - All hook examples updated from deprecated
before_everything/after_everything/on_errorkeys to the borgmatic 2.xcommands:syntax withbefore:/after:timing levels andstates:filters. - Hook script examples (
before-backup.example,after-backup.example,failed-backup.example) rewritten — label-based container selection (docker ps -f label=backup | xargs --no-run-if-empty docker stop),docker compose startfor restart,#!/bin/bashshebangs replacingwith-contenv sh. - README — added Configuration section (example files table, env var expansion, Healthchecks.io integration); Hook scripts section (label-based stop pattern, borgscripts volume, backing up Docker compose files); log rotation under Logging; expanded restore section with shell access walkthrough. All hook examples updated to
commands:syntax. .drone.yml— tags now generated dynamically fromrequirements.txtvia a prepare step writing a.tagsfile;no_cache: falseandcompress: trueremoved;PUSHRM_SHORTupdated.
borgmatic-startwrapper script — forwards SIGTERM/INT/HUP to borgmatic when the container is stopped, allowing in-progress backups to exit cleanly and release repository locks. The default cron command now usesborgmatic-startinstead ofborgmaticdirectly. A warning is printed at startup if the active crontab callsborgmaticdirectly./custom-cont-init.d/support — mount a directory of.shscripts that run after package installation but before cron starts, in filename order. Was previously documented but not wired up.svc-cron-logS6 logging pipeline — prefixes every log line from crond and borgmatic with an ISO timestamp before writing to Docker's log driver.init-custom-scriptsS6 oneshot service — the new service that executes custom init scripts, inserted betweeninit-custom-packagesandinit-config-endin the dependency chain.svc-cron/timeout-down— tells S6 to wait 5 seconds for crond to exit after SIGTERM before escalating to SIGKILL.- Renovate tracking for
S6_OVERLAY_VERSION— automatic update PRs when new S6 releases drop. stop_grace_period: 10madded todocker-compose.yml.- State volume (
/root/.local/state/borgmatic) added todocker-compose.ymland.env.template. restart: unless-stoppedadded todocker-compose.yml.version:field removed from both compose files (deprecated in Compose v2).
svc-cron/run—compgen -ereplacesset | grepfor safe secret variable expansion (immune to multi-line values); indirect expansion${!var}replaceseval;EXTRA_CRONappend now usesprintfto guarantee a leading newline; debug labels corrected to show "Before"/"After";debug_secrets()function deduplicated; deadCRON="${CRON:-...}"branch removed (S6 treats empty env vars as unset).- Default cron fallback — when no
CRONenv var and nocrontab.txtis present, the container now falls back to a default schedule of0 1 * * *with a log message, rather than silently failing to opencrontab.txt. init-custom-packages/run—EXTRA_PKGSnow split viaread -rato avoid fragile unquoted word splitting;--no-cacheadded to bothapk addcalls.svc-cron/finish— now distinguishes clean stop, error exit, and signal crash with a specific log message for each.Dockerfile— removedTERM=xtermfrom baked-inENV;S6_CMD_WAIT_FOR_SERVICES_MAXTIMEraised from0(infinite) to30000ms; S6 tarballs cleaned up after extraction;bash-docremoved; redundant secondapk upgraderemoved.- README rewritten to reflect current architecture, S6 features, signal handling, secret files, scheduling modes, Apprise notifications, and restore procedure.
.env.template— removed deprecatedVOLUME_DOT_BORGMATIC; addedVOLUME_BORGMATIC_STATE.
- CI expanded from a bare build check to a full test suite covering binary versions, config validation, S6/cron configuration, secret file expansion, custom init scripts, timestamped logging, and SIGTERM signal forwarding.
- Updated Borgmatic from v1.6.0 to v1.6.1. Read the change notes
- Updated Dockerfile to chmod script on COPY rather than via a separate chmod step.
- Updated dependencies.
- Updated Borgmatic from v1.5.24 to v1.6.0. Read the change notes
- Updated alpine to 3.15.4
- Updated alpine to 3.15.3
- Updated to latest dependencies.
- Updated Changelog
- Updated Borg backup from v1.1.17 to v1.2.0.
- Updated tags to better follow both versions of Borg and Borgmatic
- Updated Borgmatic to 1.5.23
- Updated to latest dependencies.
- Updated to latest dependencies.
- Updated Dockerfile based on the latest version.
- Updated Dockerfile based on the latest version.
- Updated to latest dependencies
- Created Dockerfile based on the latest version.
Docker-cliis installed on startup by default withLIVEINSTALLenv variable available if you wish to customise.- Added wtfc script to make sure there is an internet connection prior to running live install.
DockerCLI - 2021-09-10
- Created Dockerfile based on the latest version.
- Baked in
docker-cliduring the install
1.5.18 - 2021-09-10
- Updated readme
- Updated guide
- Updated to latest dependencies
- Added additional builds for docker-cli both as liveinstall and baked in
-
Pinned dependency versions
pipto21.2.4borgbackupto1.1.17borgmaticto1.5.18llfuseto1.4.1
-
Added
LIVEINSTALLenv variable to liveinstall tag to allow for custom installation options- If
LIVEINSTALLis not declared in your compose file thendocker-cligets installed by default.
- If
-
Added Changelog.
1.5.14 - 2021-09-10
- Cloned B3vis/Borgmatic repo
- Updated readme
- Created guide
- Added dependabot
- Added requirements.txt
- Created drone multiarch pipeline
- Added hadolint file
- List what package versions are installed in STDOUT
- Pinned dependency versions
pipto20.2.4borgbackupto1.1.16borgmaticto1.5.14llfuseto1.3.8