π― Stats powered by ClonePulse
dar-backup-image
is a Docker image that bundles the powerful dar (Disk ARchiver) utility with the robust Python wrapper dar-backup
. Together, they provide a flexible, automated, and verifiable backup solution suited for long-term data retention.
This image makes it easy to run dar-backup
in a clean, isolated container environment β perfect for use in cron jobs, systemd timers, or CI pipelines. Whether you're backing up from FUSE-based cloud storage or verifying years-old archives, this image delivers consistent, reproducible results without requiring dar or Python tooling on the host.
At it's core is dar-backup
, a Python-powered CLI that wraps dar and par2 for reliable full, differential, and incremental backups. It validates archives, performs restore tests, manages catalog databases, and optionally generates redundancy files to protect against bit rot.
π§ Highlights
Automated backup logic with dar-backup: tested, restore-verified, and redundancy-enhanced
Stateless and portable: no installation required on the host system
Ideal for FUSE filesystems: works without root, designed for user-space storage
The image automatically loads its baked-in config (/etc/dar-backup/dar-backup.conf). No --config argument is required unless you need a custom one.
Includes par2 for integrity protection
Ready for CI / cron / systemd: just mount volumes and go
The default entrypoint of this image is
dar-backup
, meaning any docker run invocation without a command will start dar-backup directly. You can also run dar, par2, or a shell interactively by overriding the entrypoint.
Use dar-backup-image
to centralize and simplify your backup operations β with restore confidence built in.
- π¦ dar-backup image for container backups and more
- ποΈ dar-backup-image
- π Table of Contents
dar
versions- Builds uploaded to Docker Hub
- π§ Hands-on Demo:
dar-backup
in a Container - Useful links
- License
- Docker Hub image repo
- Description
- Directory Layout and Permissions
- Environment Variables
- How to test
- π§ Image Tags
- π§° Volumes / Runtime Configuration
- π Usage Example
- run-backup.sh
- π Discover Image Metadata
- Image deep diving
- Common
dar-backup
commands - Using the Makefile
- Software this project benefits from
Starting with dar-backup-image
0.5.15, dar
(v2.7.18) is compiled from source rather than using Ubuntu 24.04βs older package.
Table of dar
version in dar-backup-image
tagged images:
Tag | dar |
Note |
---|---|---|
0.5.16 | 2.17.19 | Release note 2.7.19 |
0.5.15 | 2.17.18 | Release note 2.7.18 |
... - 0.5.14 | 2.17.13 | Ubuntu 24.04 standard |
dar
is compiled to provide the latest features, performance optimizations, and bug fixes (including full zstd, lz4, Argon2, GPGME, and remote repository support).
The Dockerfile verifies the source tarball using Denis Corbinβs GPG key, checks all critical features, and only includes the built binary if everything passes.
To view the embedded dar
version:
docker run -it --entrypoint /usr/local/bin/dar dar-backup:<tag> --version
Expected (abridged) output for tag 0.5.16
, confirming core capabilities:
dar version 2.7.19, Copyright (C) 2002-2025 Denis Corbin
Using libdar 6.8.3 built with compilation time options:
gzip compression (libz) : YES
Strong encryption (libgcrypt): YES
Public key ciphers (gpgme) : YES
Large files support (> 2GB) : YES
Remote repository (libcurl) : YES (HTTPS, zstd, SSH, HTTP/2)
Tag | dar-backup version |
Git Revision | Docker Hub |
---|---|---|---|
0.5.16 | 0.8.2 | 9b6dc45 | tag:0.5.16 |
0.5.15 | 0.8.2 | 3a40112 | tag:0.5.15 |
0.5.14 | 0.8.2 | eba3646 | tag:0.5.14 |
0.5.13 | 0.8.2 | ba12177 | tag:0.5.13 |
0.5.12 | 0.8.2 | 122cc02 | tag:0.5.12 |
Curious how it all works in practice?
Check out the π step-by-step demo, which walks through:
- A full backup from mounted directories
- Archive listing and contents inspection
- Selective file restore (e.g.,
.JPG
only) - Output logs, par2 generation, and verification
All performed using docker run
β no host installation required.
Topic | Link |
---|---|
dar-backup |
dar-backup on Github |
dar-backup-image |
dar-backup-image |
Docker Hub repo |
Docker Hub |
dar |
Disk ARchive |
dar-backup-image
is licensed under GPL 3
or later.
If you are unfamiliar with that license, take a look at the LICENSE file in this repo
You can see publicly available dar-backup
docker images on Docker Hub.
Those fond of curl can do this:
curl -s https://hub.docker.com/v2/repositories/per2jensen/dar-backup/tags | jq '.results[].name'
A minimal, Dockerized backup runner using dar (Disk ARchive) and dar-backup, ready for automated or manual archive creation and restore.
This is early, the dar-backup
images are not tested well, do not trust it too much. It will mature over time :-)
This image includes:
- dar
- par2
- python3
- gosu
- dar-backup (my
dar
Python based wrapper) - Clean, minimal Ubuntu 24.04 base (~170 MB)
- CIS-aligned permissions and user-drop via gosu
The run-backup.sh
script uses a standardized directory structure to ensure backups work consistently across environments. These directories are host-mounted into the container so that all backup archives, definitions, and restored files remain accessible outside Docker.
By default, the script expects (or creates) the following structure relative to WORKDIR
:
$WORKDIR/backups
β Mounted as/backups
(DAR archives and logs)$WORKDIR/backup.d
β Mounted as/backup.d
(backup definition files)$WORKDIR/data
β Mounted as/data
(source data to back up)$WORKDIR/restore
β Mounted as/restore
(restored files)
The script resolves its directory paths in the following priority order:
- If an explicit directory environment variable is set (
DAR_BACKUP_DIR
,DAR_BACKUP_D_DIR
,DAR_BACKUP_DATA_DIR
, orDAR_BACKUP_RESTORE_DIR
), that value is used. - Otherwise, if
WORKDIR
is set, each directory defaults to a subdirectory ofWORKDIR
(e.g.,$WORKDIR/backups
). - If neither is set, the script defaults to using the directory where
run-backup.sh
resides as the base.
This allows full flexibility: you can set WORKDIR
once for a standard layout, or override specific directories individually.
All files written by dar-backup
inside the container will match your host user and group:
-
By default,
RUN_AS_UID
andRUN_AS_GID
are set to your current UID and GID. -
These are passed to Docker via
--user "$RUN_AS_UID:$RUN_AS_GID"
. -
Running as
root
(UID 0) is disallowed; the script will exit with an error. -
You can override these values for special cases, such as:
-
Group-based backups:
RUN_AS_GID=$(getent group backupgrp | cut -d: -f3)
Ensures all archives are group-writable. -
Service accounts:
RUN_AS_UID=1050 RUN_AS_GID=1050 ./run-backup.sh -t FULL
Matches ownership for automated or scheduled jobs.
-
Here are three common configurations, depending on your use case:
For single-user systems:
WORKDIR=$HOME/dar-backup
$HOME/dar-backup/backups # DAR archives and logs
$HOME/dar-backup/backup.d # Backup definition files
$HOME/dar-backup/data # Source data to back up
$HOME/dar-backup/restore # Restored files
Permissions: Owned entirely by your user (default behavior).
For teams or shared servers, use a group to manage permissions:
WORKDIR=/srv/dar-backup
Group ownership
chown -R :backupgrp /srv/dar-backup
chmod -R 2770 /srv/dar-backup
Environment for group-based runs:
RUN_AS_UID=$(id -u)
RUN_AS_GID=$(getent group backupgrp | cut -d: -f3)
All members of backupgrp
can write backups while keeping data private.
For scheduled or service-based backups:
WORKDIR=/mnt/backups
Owned by a dedicated backup user (UID 1050:GID 1050)
Run with:
RUN_AS_UID=1050 RUN_AS_GID=1050 ./run-backup.sh -t FULL
This ensures consistent ownership for cron jobs or automated workflows.
For full environment variable documentation, see the header comments in run-backup.sh
.
Hereβs a quick reference for all environment variables used by the script:
Variable | Default | Purpose |
---|---|---|
IMAGE |
dar-backup:dev |
Docker image tag to use for the backup container. |
WORKDIR |
Script directory | Base directory for all backup-related paths. |
RUN_AS_UID |
Current user's UID | UID passed to Docker to avoid root-owned files. |
RUN_AS_GID |
Current user's GID | GID passed to Docker for correct file group ownership. |
DAR_BACKUP_DIR |
$WORKDIR/backups |
Host directory for DAR archives and logs (mounted at /backups ). |
DAR_BACKUP_D_DIR |
$WORKDIR/backup.d |
Host directory for backup definition files (mounted at /backup.d ). |
DAR_BACKUP_DATA_DIR |
$WORKDIR/data |
Host directory containing source data (mounted at /data ). |
DAR_BACKUP_RESTORE_DIR |
$WORKDIR/restore |
Host directory for restored files (mounted at /restore ). |
For details on behavior, UID/GID handling, and usage examples, see the comments in run-backup.sh
.
# make new base and development image
make all-dev
# run FULL, DIFF and INCR backups in a temp directory
make test
Two images are built:
-
A base image which currently is a slimmed down ubuntu 24.04 image
-
dar-backup image is installed on top of the base image
Some images are put on DockerHub.
The Release procedure results in two things:
- An image pushed to Docker Hub.
- Metadata about the image put in build-history.md.
For now I am not using latest
, as the images have not yet demonstrated their quality.
I am currently going with:
Tag | Description | Docker Hub | Example Usage |
---|---|---|---|
:0.x.y |
Versioned releases following semantic versioning | β Yes | docker pull per2jensen/dar-backup:0.5.6 |
:stable |
Latest "good" and trusted version; perhaps :rc |
β Yes | docker pull per2jensen/dar-backup:stable |
:dev |
Development version; may be broken or incomplete | β No | docker run dar-backup:dev |
The default dar-backup.conf baked into the image assumes the directories mentioned below.
The locations should be mounted with actual directories on your machine for backups.
Directories in file system | Directories in container | Purpose |
---|---|---|
/some/dir/to/backup/ | /data |
Source directory for backup |
/keep/backups/here/ | /backup |
dar archives and .par2 files are put here |
/restore/tests/ | /restore |
Optional restore target |
/backup/definitions/ | /backup.d |
Contains backup definition files |
The mapping between physical directories on your file system and the expected directories inside the container is performed by the -v /physical/dir:/container/dir
options (see example below).
Determine if you want to built an image yourself, or use one of mine from Docker Hub.
# make a container
$ make FINAL_VERSION=dev DAR_BACKUP_VERSION=0.8.0 dev # make a local development image
# check
$ docker images |grep "dev"
dar-backup dev e72a7fd82a4b 19 seconds ago 174MB
# Set IMAGE to your own
export IMAGE=dar-backup:dev # your own locally build image
# Or set IMAGE to one of mine on Docker Hub
VERSION=0.5.16; export IMAGE=per2jensen/dar-backup:${VERSION}
Now run dar-backup
in the container
# Run it (from script or manually)
# Configuration
export DATA_DIR=/tmp/test-data # the data to backup
export BACKUP_DIR=/tmp/test-backups # the directory that keeps the backups
export RESTORE_DIR=/tmp/test-restore # the directory used for restore tests during backup verification
export BACKUP_D_DIR=/tmp/test-backup.d # the directory keeping the `backup definitions`
docker run --rm \
-e RUN_AS_UID=$(id -u) \
-v "$DATA_DIR":/data \
-v "$BACKUP_DIR":/backup \
-v "$RESTORE_DIR":/restore \
-v "$BACKUP_D_DIR":/backup.d \
"$IMAGE" \
-F --log-stdout
The image automatically uses /etc/dar-backup/dar-backup.conf
unless you override it.
To use another config file you have multiple options:
- Modify the baked-in and build a new image.
- Use --config option to point to another (for example: /backup/dar-backup.conf, which in the example above means you physically put it on "$BACKUP_DIR"/dar-backup.conf)
- Let DAR_BACKUP_CONFIG point to a config file.
The container uses set-priv to drop root privileges. Pass -e RUN_AS_UID=$(id -u) to run as your own user inside the container.
This script runs a backup using a dar-backup Docker image.
It runs a backup based on the specified type (FULL, DIFF, INCR) with the following features:
Using the baked in dar-backup.conf file (se more here).
Uses the .darrc file from the PyPI package added to the image, see image details here
.darrc contents
It print log messages to stdout.
Expected directory structure when running this script:
WORKDIR/
βββ backups/ # Where backups are stored
βββ backup.d/ # Backup definitions
βββ data/ # Data to backup
βββ restore/ # Where restored files will be placed
If envvar WORKDIR is set, the script uses that as the base directory.
If WORKDIR is not set, the script uses the directory where the script is located as the base directory.
These directories are host-mounted into the container so your data and archives remain accessible:
Host Directory (default) | Container Mount | Purpose |
---|---|---|
$WORKDIR/backups |
/backups |
DAR archives and log files |
$WORKDIR/backup.d |
/backup.d |
Backup definition files |
$WORKDIR/data |
/data |
Source data for the backup |
$WORKDIR/restore |
/restore |
Destination for restore tests |
You can override any of these paths by setting the environment variables:
DAR_BACKUP_DIR
, DAR_BACKUP_D_DIR
, DAR_BACKUP_DATA_DIR
, DAR_BACKUP_RESTORE_DIR
.
If none are set, WORKDIR
(or the scriptβs own directory) is used as the base.
More info on backup definitions in general
View supplied default
backup definition
If IMAGE is not set, the script defaults to dar-backup:dev
.
You can see available images on [Docker Hub here)(https://hub.docker.com/r/per2jensen/dar-backup/tags)
If RUN_AS_UID is not set, it defaults to the current user's UID. - running the script as root is not allowed, the script will exit with an error.
The run-backup.sh
script supports selecting a specific backup definition file, which allows you to maintain multiple dataset or policy definitions.
Each backup definition file resides in:
$DAR_BACKUP_D_DIR (default: $WORKDIR/backup.d)
and is automatically mounted into the container at /backup.d
.
Note on daily backups per definition:
dar-backup
will only create oneFULL
, oneDIFF
, and oneINCR
backup per definition per calendar day.
- You can run all three types (FULL β DIFF β INCR) on the same day.
- A second run of the same type (e.g., another FULL) will be skipped to avoid overwriting the existing archive.
- To force a new run, either remove or archive the previous
.dar
files for that definition/date. -- Usecleanup
for safe archive deletions
To specify a backup definition, use the -d
or --backup-definition
option:
WORKDIR=/path/to/workdir ./run-backup.sh -t FULL -d my-dataset
This instructs dar-backup
to load:
$DAR_BACKUP_D_DIR/my-dataset
instead of the default definition (default
).
If no -d
option is supplied, the script falls back to the default definition.
The run-backup.sh
script also generates a minimal default definition file at:
$DAR_BACKUP_D_DIR/default
if none exists.
The script passes the chosen definition to dar-backup
using:
--backup-definition "<name>"
This is achieved dynamically using:
${BACKUP_DEF:+--backup-definition "$BACKUP_DEF"}
in the docker run
command, which:
- Adds
--backup-definition "<name>"
ifBACKUP_DEF
is non-empty. - Skips it entirely if no
-d
was provided (dar-backup then uses the default definition).
- Create a new definition file:
echo "-R /data/projects -z5 -am --slice 5G" > $HOME/dar-backup/backup.d/projects
- Run a differential backup using it:
WORKDIR=$HOME/dar-backup ./run-backup.sh -t DIFF -d projects
The backup will:
- Store archives in
$HOME/dar-backup/backups
. - Use
/backup.d/projects
as the definition. - Retain ownership based on
RUN_AS_UID
andRUN_AS_GID
.
Why is my backup skipped?
Only oneFULL
, oneDIFF
, and oneINCR
backup can be created per definition per day.
If a run is skipped, remove or archive the existing.dar
files for that definition/date.Permission issues on host files?
EnsureRUN_AS_UID
andRUN_AS_GID
match the desired owner.
If unsure, runid -u
andid -g
to get your UID and GID.Definition not found?
Make sure yourbackup.d/<name>
file exists (or let the script auto-createdefault
).
WORKDIR=/path/to/your/workdir IMAGE=`image` ./run-backup.sh -t FULL|DIFF|INCR -d "backup_definition"
Learn what's inside the dar-backup
image: program versions, build metadata, and available versions.
Run the image with different entrypoints to check the bundled versions of dar-backup
, dar
, and par2
:
VERSION=0.5.16; IMAGE=per2jensen/dar-backup:${VERSION}
# dar-backup version
docker run --rm --entrypoint "dar-backup" "$IMAGE" -v
# dar version
docker run --rm --entrypoint dar "$IMAGE" --version
# par2 version
docker run --rm --entrypoint par2 "$IMAGE" --version
# Or get them all in one go:
docker run --rm --entrypoint "" "$IMAGE" \
bash -c "dar-backup -v; dar --version; par2 --version"
VERSION=0.5.16; docker pull per2jensen/dar-backup:${VERSION}
docker inspect per2jensen/dar-backup:${VERSION} | jq '.[0].Config.Labels'
Example output:
{
"org.opencontainers.image.base.created": "2025-06-19T13:38:32Z",
"org.opencontainers.image.created": "2025-06-19T13:38:32Z",
"org.opencontainers.image.description": "Container for DAR-based backups using dar-backup",
"org.opencontainers.image.ref.name": "ubuntu",
"org.opencontainers.image.source": "https://hub.docker.com/r/per2jensen/dar-backup",
"org.opencontainers.image.version": "0.5.1"
}
# Show first 100 available tags
curl -s 'https://hub.docker.com/v2/repositories/per2jensen/dar-backup/tags?page_size=100' \
| jq -r '.results[].name' | sort -V
Although dar-backup
is the primary CLI inside the container, you can also run dar directly from the image to take manual backups or inspect archives β perfect for advanced workflows or testing.
Here's a minimal example of how to use dar directly:
export DATA_DIR=/tmp/test-data
export BACKUP_DIR=tmp/test-backups
VERSION=0.5.16; export IMAGE=per2jensen/dar-backup:${VERSION}
touch /tmp/test-data/TEST.txt
docker run --rm -v "$DATA_DIR":/data -v "$BACKUP_DIR":/backup --entrypoint dar "$IMAGE" -c /backup/myarchive -R /data
Example output
No terminal found for user interaction. All questions will be assumed a negative answer (less destructive choice), which most of the time will abort the program.
--------------------------------------------
1 inode(s) saved
including 0 hard link(s) treated
0 inode(s) changed at the moment of the backup and could not be saved properly
0 byte(s) have been wasted in the archive to resave changing files
0 inode(s) with only metadata changed
0 inode(s) not saved (no inode/file change)
0 inode(s) failed to be saved (filesystem error)
0 inode(s) ignored (excluded by filters)
0 inode(s) recorded as deleted from reference backup
--------------------------------------------
Total number of inode(s) considered: 1
--------------------------------------------
EA saved for 0 inode(s)
FSA saved for 1 inode(s)
--------------------------------------------
This shows that even without dar-backup, you can still invoke dar manually β helpful for debugging, recovery scenarios, or power-user workflows.
π§ Tip: You can also run par2 directly using --entrypoint par2 if needed.
dar-backup --full-backup
dar-backup --differential-backup
dar-backup --incremental-backup
dar-backup --list
dar-backup --list-contents <archive_name>
dar-backup --restore <archive_name>
The Makefile
automates building, testing, and releasing the dar-backup-image
Docker images.
It supports local development builds, final version tagging, and release workflows (including Docker Hub pushes).
Target | What It Does |
---|---|
make dev |
Builds a development image (dar-backup:dev ) using the local Dockerfile and configuration. |
make all-dev |
Builds both the base image and the dar-backup:dev image (default dependency for most other targets). |
make test |
Builds dar-backup:dev (via all-dev ) and runs the full pytest suite against it. |
make FINAL_VERSION=x.y.z final |
Tags the current dar-backup:dev as dar-backup:x.y.z and verifies version/labels. |
make FINAL_VERSION=x.y.z test |
Builds (or re-tags) dar-backup:x.y.z , then runs pytest against it. |
make IMAGE=per2jensen/dar-backup:x.y.z test-pulled |
Pulls the specified released image from Docker Hub and tests it (skips local build). |
make FINAL_VERSION=x.y.z DAR_BACKUP_VERSION=a.b.c dry-run-release |
Creates a detached worktree, builds the image as dar-backup:x.y.z , runs tests, verifies labels, but does not push to Hub. |
make FINAL_VERSION=x.y.z DAR_BACKUP_VERSION=a.b.c release |
Builds, verifies, tests, and pushes the final image to Docker Hub, also updating doc/build-history.json and READNE.md . |
make size-report |
Displays a normalized report of image layer sizes (for auditing image size). |
make dev-nuke |
Cleans all cached layers and build artifacts (forces a full fresh build next time). |
During development, build and test the local dar-backup:dev
image:
make dev # Build dar-backup:dev
make test # Run tests against dar-backup:dev
To test a specific local version (tagged dar-backup:x.y.z):
make FINAL_VERSION=0.5.15 test
After publishing a release, test the exact image on Docker Hub (ignoring local builds):
make IMAGE=per2jensen/dar-backup:0.5.15 test-pulled
This:
Pulls the image from Docker Hub.
Runs the full pytest suite (no local build).
Dry-run the release (build & test only, no push):
make FINAL_VERSION=0.5.15 DAR_BACKUP_VERSION=0.8.2 dry-run-release
This validates:
The image builds correctly.
Labels and dar-backup --version match.
All tests pass.
Perform the actual release (push to Docker Hub):
export DOCKER_USER=your-username
export DOCKER_TOKEN=your-access-token # do not put token in bash_history
make FINAL_VERSION=0.5.15 DAR_BACKUP_VERSION=0.8.2 release
The release target will:
Build and tag dar-backup:0.5.15.
Verify labels and CLI version.
Run tests.
Push the image to Docker Hub.
Update doc/build-history.json.
During development:
make dev && make test
Before release:
make dev-nuke
make FINAL_VERSION=x.y.z test (validate your local final image)
Dry-run release:
make FINAL_VERSION=x.y.z DAR_BACKUP_VERSION=a.b.c dry-run-release
Push the final image:
make FINAL_VERSION=x.y.z DAR_BACKUP_VERSION=a.b.c release
Verify the published image:
make IMAGE=per2jensen/dar-backup:x.y.z test-pulled