-
v0.9.0
StableAll checks were successfulCI / Build / Publish / Validate CHANGELOG structure (push) Successful in 2sCI / Build / Publish / Shellcheck (push) Successful in 3sCI / Build / Publish / Round-trip + scheduler smoke (push) Successful in 2m44sCI / Build / Publish / Workflow summary (push) Successful in 1sCI / Build / Publish / Bats unit tests (push) Successful in 6sCI / Build / Publish / PR build & smoke test (push) Has been skippedCI / Build / Publish / Host-driven integration (entrypoint + storage sidecars) (push) Successful in 1m46sCI / Build / Publish / Build multi-arch and push (push) Successful in 3m51sCI / Build / Publish / Reconcile releases across registries (push) Successful in 1m21sreleased this
2026-07-09 21:49:05 -07:00 | 36 commits to main since this releaseBreaking
BUNDLE_PASSWORDis no longer read from the environment and must be provided as a file (a Docker or Kubernetes secret). Every existing deployment that passes it viaenvironment:orenv_filemust migrate before upgrading: write the password to a file, mount it at/run/secrets/bundle_password(a Docker Compose or Swarmsecrets:entry namedbundle_passwordlands there automatically — see thesecrets:block in the repo'scompose.yaml), or pointBUNDLE_PASSWORD_FILEat another path, and removeBUNDLE_PASSWORDfrom the environment. A container that still has it set fails fast at startup with this migration message rather than silently ignoring it (which would otherwise drop a bundle deployment into env-native mode and fail later with a confusing "no bundle" error). Why: the bundle password decrypts the entire bundle, including the RSA private key, and an environment variable leaks throughdocker inspectand/proc.
Added
- Env-native configuration as a dual-mode alternative to the encrypted bundle, so a deployment can be driven entirely by environment variables plus file-based secrets (for example a Kubernetes ConfigMap + Secret) with no bundle at all. Configuration is now resolved in layers of increasing precedence: the decrypted bundle's
config.sh(optional baseline, still the cold-restore path), plain environment variables for non-secret settings, then files for secrets. Non-secret settings (SERVICE_DIRECTORIES, theSTORAGE_TARGET_N_*non-secret fields,ROTATE_BACKUPS,PRUNE_KEEP,DUPLICACY_THREADS,NOTIFICATION_SERVICE) can be set as env vars and override the bundle. Secrets (STORAGE_PASSWORD,RSA_PASSPHRASE,PUSHOVER_USER_KEY,PUSHOVER_API_TOKEN, and each target'sB2_ID/B2_KEY/S3_ID/S3_SECRET) are read only from files, never from a raw env var: each comes from<NAME>_FILEif set, otherwise/run/secrets/<lowercased name>, and a secret passed as a raw env var is purged, with a logged warning naming the file path to use instead. The RSA keypair (and optional SFTP key) are supplied the same way and materialized into/opt/archiver/keysat startup, with mounted key files overriding bundle-extracted keys. This lets you migrate off the bundle one value at a time: set an env var or mount a secret and it shadows the bundle, and once every value is shadowed you can drop the bundle. With no env or secret overrides and a bundle present, behavior is unchanged. SERVICE_DIRECTORIESaccepts a colon-delimited scalar (for example/srv/*/:/home/user/data/) in addition to the bundle's bash-array form, so it fits in a single env var or a YAML block. Newlines are also accepted as separators.archiver migrate [OUTPUT_DIR]converts a bundle-based deployment to env-native materials: it writes the effective non-secret settings to an env file and every secret (plus the keys) to its own file, ready to load as a Docker Composeenvironment:block or a Kubernetes ConfigMap, and as Docker/Kubernetes secrets or openbao entries. The bundle is left untouched.bundle exportis now mode-agnostic: it serializes the effective (env-merged) configuration into the bundle, so a fully env-native deployment can regenerate a portable, self-contained encrypted bundle for cold restore / disaster recovery.archiver initnow also writes ready-to-deploy env-native materials toenv-native/in the bundle directory (archiver.env+secrets/, via the same serializer asarchiver migrate), alongside the encrypted bundle. A new deployment can go straight to env-native without ever hand-transcribing configuration; the emitted files are plaintext secrets, so move them into a secret store and delete the directory. The shippedcompose.yamltemplate now leads with the env-native service; bundle mode is the commented alternative.- Broad new test coverage, all in CI: secondary-storage copy path incl. restore-from-secondary and copy-failure reporting; multi-service glob expansion with per-service failure isolation; lock concurrency (busy refusal, stale-lock recovery); real-entrypoint
docker stopduring a backup; pause/resume/stop-while-paused against the live process tree; stop during a non-final service; per-serviceservice-backup-settings.sh(filters, hook order, isolation); healthcheck states; restore extras (revision pinning,IGNORE_OWNERSHIP,restore-service.shhook); malformedCRON_SCHEDULEfail-fast; real s3 (MinIO sidecar, TLS) and sftp (sshd sidecar) backup+restore runtime round-trips; and new bats suites forlockfile.sh,SERVICE_DIRECTORIESexpansion, config serialization round-trips, andformat_duration.
Deprecated
- Deploying from the encrypted bundle is now the transitional path: env-native configuration (environment variables plus file-based secrets) is the primary, first-class configuration source, and a future release will drop bundle support entirely. The bundle remains fully supported throughout 0.9.x, both for deployment and as the
bundle exportcold-restore artifact.
Changed
- Startup diagnostics for the file-only secret model: a mounted bundle whose password cannot be resolved now fails fast naming the expected password path (instead of a misleading "no bundle" error, or silently starting env-native and never backing up when keys are also mounted); an explicitly set
BUNDLE_PASSWORD_FILEor<NAME>_FILEpointing at a missing file is a hard error instead of a silent fallback; secret files with Windows line endings (CRLF) are handled instead of failing later as a wrong password; and missing B2/S3 credentials name the secret file to provide. - The bundle password is no longer left in the container's process environment after the bundle import completes, and no longer appears on the openssl command line (visible in
/procwhile it runs) during bundle import/export. TheENV BUNDLE_PASSWORD=""stub left in the image (visible indocker inspect) is gone. archiver bundle importnow works from a plaindocker exec(it resolves the default bundle path and the password file itself, matchingbundle export).- The interactive
archiver restorehonorsIGNORE_OWNERSHIP=1like the non-interactive restore paths. archiver migrateandarchiver initwrite their env-native materials with restrictive permissions from the start (umask), and both warn that the output holds plaintext secrets to be moved into a secret store.config-loadernow fails fast with a clear message whenSTORAGE_PASSWORDis shorter than 8 characters (a Duplicacy storage-init requirement), instead of letting it surface later as an opaque "storage initialization failed".bundle importnow treats the SSH keypair as optional (only sftp targets need it); the RSA keypair is what it requires. This lets a bundle exported from an env-native deployment that has no SSH key re-import cleanly.- Restore now logs a warning (instead of doing it silently) when it would preserve original ownership but the container lacks
CHOWN/FOWNER, so restored files would land owned by root. This makes it safe to run a backup-only deployment with justDAC_OVERRIDEand addCHOWN+FOWNERonly when restoring. The restore is never blocked; setIGNORE_OWNERSHIP=1to restore without preserving ownership.
Fixed
- A failed primary backup is now detected and reported. The backgrounded backup pipeline captured the log reader's PID instead of duplicacy's, so a duplicacy failure was invisible: the run logged the backup as completed, sent a success notification, and exited 0. A failed backup now logs an error, is counted in the completion notification, skips that service's copy/wrap-up, and makes
archiver backupexit non-zero. archiver stoprequested during the final service's backup now actually stops the run. Previously that path swallowed the stop: the run continued into the storage check,prune -exhaustive, and copies to secondary storages, then recorded a successful completion. A stop during any service's backup now terminates duplicacy directly (previously it killed the log reader and waited for duplicacy to die of a broken pipe), skips the storage wrap-up, and records "Backup stopped" without counting the stop as an error.- When no service completes a backup, the storage wrap-up (check/prune) is skipped instead of silently running from whatever repository directory the last failed service left as the working directory.
- Stale-lock recovery now actually re-takes the lock. Previously a run that cleaned up a dead-PID lock file proceeded without holding the lock itself, so
stop/status/pausecould not see it and a concurrent backup would have been admitted against the same storage. - A backup refused because one is already running now says so (
A backup is already running (PID ...)) instead of exiting 1 silently. - The container's SIGTERM handler (
docker stop) now waits for the interrupted backup to record its stop and release the lock before exiting, instead of racing PID-namespace teardown against that cleanup (which could SIGKILL it mid-flight and lose the stop record/notification). archiver healthcheckno longer reports a healthy env-native deployment as UNHEALTHY: the absence ofconfig.shis only an error when there is no RSA key either. It also detected "run finished despite errors" by grepping for a log line that nothing ever emitted, so any transient error in the recent log window flipped the container UNHEALTHY; it now matches the real end-of-run marker.- Env-native SFTP deployments can now restore: the entrypoint places
/run/secrets/ssh_public_key(overrideSSH_PUBLIC_KEY_FILE) alongside the private key, andarchiver migrateexports it. The SFTP restore path requires both key halves; previously only the private key was placed, so backup worked but restore failed.
Dependencies
- chore(deps): update dependency aptible/supercronic to v0.2.47
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads