Back to README
dar-backup ships a purpose-built metrics dashboard — a single HTML file that
queries the metrics database through Datasette and renders a summary of your
most recent backup runs directly in the browser. No extra services, no
framework, no build step.
The full dashboard shows multiple backup definitions side by side, including how a failed run is surfaced — highlighted in amber so it stands out immediately:
At the top, summary metric cards give an instant health overview:
- Total runs recorded
- Success rate across all runs shown
- Number of distinct backup definitions
- Total failed inodes across all runs shown — the key indicator for FUSE-mounted storage issues such as the pCloud Crypto Folder (see Troubleshooting: error code 5)
- Status and timestamp of the most recent run
Below the summary, each backup definition gets its own section showing the last three runs in a table with the following columns:
| Column | Description |
|---|---|
| Started | Timestamp of the run |
| Type | FULL, DIFF, or INCR |
| Status | SUCCESS, WARNING, or FAILURE pill — colour coded |
| Duration | Total wall-clock time; hover for the dar / verify / par2 breakdown |
| Archive size | Total size of all .dar slices |
| Inodes saved / failed | Files saved and files that could not be saved — failed inodes are highlighted in amber or red |
| Not saved | Files unchanged since the last backup (expected for DIFF/INCR) |
| Excluded | Files skipped by filters |
| Phases | ✓ / ✗ for verify, restore test, and par2 |
| Error | First error message from the run, if any |
Rows with failed inodes are highlighted with a left amber border so they stand out at a glance without needing to read every cell.
Below the recent-run tables, one trend chart is rendered per backup definition, covering the full run history in the database. See Reading the trend charts below.
Each chart plots archive size over time, grouped by time period (week / month / year — selectable via the toggle above the charts).
Every chart shows two datasets overlaid on the same axes:
The indigo line shows the size of the most recent FULL backup, stepped forward into subsequent periods until the next FULL replaces it.
- A dot is drawn only at periods where a FULL backup actually ran.
- Carry-forward periods have the same Y value but no visible dot.
- This gives a stable capacity baseline — the true size of the entire source data set at the last full snapshot.
Each cyan dot represents the sum of all DIFF and INCR archive sizes in that period. Ten 24 GB INCR runs in one week appear as a single dot at ~240 GB, not ten separate dots.
Comparing the cyan dot height against the indigo step shows at a glance how much incremental activity happened relative to the full data set size.
Each dot reflects the worst status of the runs that make up that dot:
| Dot appearance | Meaning |
|---|---|
| Small green dot | All runs in the period succeeded |
| Medium amber dot | At least one run ended with a WARNING |
| Large red dot with white border | At least one run failed — investigate immediately |
The white border on failure dots makes them stand out even when the chart is small or printed.
Hovering a dot (or anywhere on the chart area) shows a combined tooltip for that period. Two example tooltips:
A week with ten INCR runs and no FULL:
2025-W11
FULL: 1.84 TB (carried fwd)
DIFF/INCR: 238.6 GB · 10 runs
Status: SUCCESS
10 runs (INCR)
A month where a FULL ran alongside some DIFFs, with one warning:
2025-03
FULL: 1.91 TB
DIFF/INCR: 42.3 GB · 3 runs
Status: WARNING
4 runs (FULL, DIFF)
If any run in the period failed, an extra line appears:
⚠ 1 failed run
Use the Weekly / Monthly / Yearly buttons to zoom in or out. Monthly is the default and works well for most setups. Yearly is useful when you have several years of history and want to see the long-term growth trend at a glance.
. venv/bin/activate # activate the virtual environment dar-backup in installed in
pip install dar-backup[dashboard]This installs datasette as an optional dependency alongside dar-backup.
The dashboard HTML is bundled inside the package — no separate download needed.
# if venv is not activated
. venv/bin/activate # activate the virtual environment dar-backup in installed in
dar-backup-dashboardThat is all. The command:
- Reads
METRICS_DB_PATHfrom your dar-backup config file (same config resolution asdar-backupitself:--config-file→DAR_BACKUP_CONFIG_FILEenv var →~/.config/dar-backup/dar-backup.conf) - Starts Datasette on port 8001 (or the next free port if 8001 is taken)
- Waits for Datasette to be ready, printing a dot per second so you know something is happening
- Opens the dashboard in your default browser
# Use config file from a non-default location
dar-backup-dashboard -c /etc/dar-backup/dar-backup.conf
# Point directly at a specific database
dar-backup-dashboard --db ~/dar-backup/dar-backup-metrics.db
# Start on a different port
dar-backup-dashboard --port 8010
# Headless — print URL only, useful in scripts
dar-backup-dashboard --no-browserThe dashboard is a single self-contained HTML file bundled in
dar_backup/data/dashboard.html. When dar-backup-dashboard starts, it
launches Datasette with --static dashboard:<html_dir> so the file is served
at http://127.0.0.1:<port>/dashboard/dashboard.html. The Datasette base URL
is appended as a ?datasette= query parameter, so the dashboard connects to
the right instance automatically — no manual configuration in the browser UI
is needed.
The dashboard queries the metrics database directly via Datasette's JSON API
(/<db>.json?sql=...). The Datasette URL field at the top of the page can be
edited and the Refresh button clicked if you want to point the dashboard at a
different Datasette instance.
Press Ctrl+C in the terminal to shut down Datasette when you are done.
Every backup run appends one row to the backup_runs table in the SQLite
metrics database. The database is created automatically on first use and
existing databases are migrated silently — columns added in later releases
are appended with ALTER TABLE ADD COLUMN so no data is lost.
Errors writing metrics are logged as WARNING and never abort a backup.
| Column | Type | Description |
|---|---|---|
id |
INTEGER | Auto-incrementing primary key. |
backup_definition |
TEXT | Name of the backup definition file. |
backup_type |
TEXT | FULL, DIFF, or INCR. |
archive_name |
TEXT | Base name of the dar archive, e.g. homedir_FULL_2025-11-22. |
hostname |
TEXT | Hostname of the machine that ran the backup (socket.gethostname()). |
dar_backup_version |
TEXT | dar-backup version string. |
dar_version |
TEXT | dar version string. |
| Column | Type | Description |
|---|---|---|
run_started_at |
TEXT | ISO-8601 timestamp when the backup started. |
run_finished_at |
TEXT | ISO-8601 timestamp when the full run finished (including verify and par2). |
duration_secs |
REAL | Total wall-clock seconds for the entire run. |
dar_duration_secs |
REAL | Seconds spent in the dar backup phase only. |
verify_duration_secs |
REAL | Seconds spent in the verify phase. |
par2_duration_secs |
REAL | Seconds spent generating par2 redundancy files. |
| Column | Type | Description |
|---|---|---|
status |
TEXT | SUCCESS, WARNING, or FAILURE. |
dar_exit_code |
INTEGER | Raw exit code returned by dar (0 = success, 5 = some files skipped due to filesystem errors, etc.). |
failed_phase |
TEXT | DAR, VERIFY, or PAR2 — set when a phase fails; NULL on success. |
error_summary |
TEXT | Short human-readable description of the first error encountered, or NULL. |
catalog_updated |
INTEGER | 1 if the dar manager catalog was updated successfully, 0 otherwise. |
verify_passed |
INTEGER | 1 if the archive integrity test passed. |
restore_test_passed |
INTEGER | 1 if the restore test passed. |
par2_passed |
INTEGER | 1 if par2 file generation succeeded. |
| Column | Type | Description |
|---|---|---|
archive_size_bytes |
INTEGER | Total size of all .dar slice files in bytes. |
num_slices |
INTEGER | Number of dar archive slices. |
par2_size_bytes |
INTEGER | Total size of all .par2 files in bytes. |
files_verified |
INTEGER | Number of files verified during the verify phase. |
backup_dir_free_bytes |
INTEGER | Free space on the backup destination at run end. |
These columns are parsed from the summary block dar prints at the end of every
run (visible in the *-commands.log). If dar changes its output format or the
run aborts before the summary is printed, the value is stored as NULL — the
backup is never affected.
--------------------------------------------
6603 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
24695 inode(s) not saved (no inode/file change)
5 inode(s) failed to be saved (filesystem error)
9 inode(s) ignored (excluded by filters)
0 inode(s) recorded as deleted from reference backup
--------------------------------------------
Total number of inode(s) considered: 31312
--------------------------------------------
EA saved for 0 inode(s)
FSA saved for 0 inode(s)
| Column | Type | dar output line |
|---|---|---|
inodes_saved |
INTEGER | N inode(s) saved |
hard_links_treated |
INTEGER | including N hard link(s) treated |
inodes_changed_during_backup |
INTEGER | N inode(s) changed at the moment of the backup… |
bytes_wasted |
INTEGER | N byte(s) have been wasted in the archive… |
inodes_metadata_only |
INTEGER | N inode(s) with only metadata changed |
inodes_not_saved |
INTEGER | N inode(s) not saved (no inode/file change) |
inodes_failed |
INTEGER | N inode(s) failed to be saved (filesystem error) — non-zero triggers dar exit code 5 |
inodes_excluded |
INTEGER | N inode(s) ignored (excluded by filters) |
inodes_deleted |
INTEGER | N inode(s) recorded as deleted from reference backup |
inodes_total |
INTEGER | Total number of inode(s) considered: N |
ea_saved |
INTEGER | EA saved for N inode(s) |
fsa_saved |
INTEGER | FSA saved for N inode(s) |
The metrics database is populated automatically going forward from the first backup run after the feature was introduced. Archives that predate the metrics feature are invisible to the trend charts.
v2/scripts/import-archive-metrics.py fills that gap. It scans a directory of
existing .dar archives and imports one row per archive into the metrics DB —
without re-running any backups.
| Field | Source |
|---|---|
backup_definition, backup_type |
Parsed from the archive filename |
archive_name, run_started_at |
Parsed from the archive filename |
archive_size_bytes, num_slices |
Summed from the .N.dar slice files on disk |
status |
Always SUCCESS — the archive exists, so the backup completed |
Inode stats (inodes_saved etc.) |
Attempted via dar -l; left NULL if not available |
Timing fields (duration_secs, phase durations), verification results
(verify_passed, restore_test_passed, par2_passed), and error details are
always NULL for historical imports — they were never recorded by dar.
Archives already present in the DB (matched by archive_name) are silently
skipped. The script is safe to run repeatedly as new archives accumulate —
only genuinely new archives are inserted.
Run the script from inside the dar-backup virtualenv so that
dar_backup.util.ensure_metrics_db is importable and the full schema
(including all migration columns) is guaranteed:
cd ~/git/dar-backup/v2
source venv/bin/activate
# Preview what would be imported (no DB changes)
python scripts/import-archive-metrics.py \
--archive-dir /path/to/archives \
--metrics-db /path/to/dar-backup-metrics.db \
--dar ~/.local/dar/bin/dar \
--dry-run
# Import for real
python scripts/import-archive-metrics.py \
--archive-dir /path/to/archives \
--metrics-db /path/to/dar-backup-metrics.db \
--dar ~/.local/dar/bin/darThe script processes archives in chronological order and commits each row individually, so a failure partway through leaves the DB consistent. Re-running picks up where it left off.
Once imported, the historical archives appear in the trend charts alongside live backup runs. The chart always renders in chronological order regardless of the order rows were inserted — so mixing old imports with new live rows works correctly.
Datasette is a lightweight, zero-configuration tool for exploring SQLite databases through a web browser. It requires no server setup — just point it at the metrics database and it renders tables, runs SQL queries, and produces charts instantly.
pip install datasettedatasette ~/dar-backup/dar-backup-metrics.dbOpen http://127.0.0.1:8001 in a browser. Datasette auto-detects all tables
and indexes.
Browse all runs for a specific backup definition, newest first:
SELECT archive_name, backup_type, status, dar_exit_code,
duration_secs, inodes_saved, inodes_failed, inodes_total
FROM backup_runs
WHERE backup_definition = 'homedir'
ORDER BY run_started_at DESC;Find all runs where files were skipped due to filesystem errors (dar exit code 5):
SELECT run_started_at, backup_definition, backup_type,
inodes_failed, inodes_total, error_summary
FROM backup_runs
WHERE inodes_failed > 0
ORDER BY run_started_at DESC;Track backup size growth over time:
SELECT run_started_at, backup_type, archive_size_bytes,
backup_dir_free_bytes
FROM backup_runs
WHERE backup_definition = 'homedir'
ORDER BY run_started_at;Show average backup duration per definition and type:
SELECT backup_definition, backup_type,
ROUND(AVG(dar_duration_secs), 1) AS avg_dar_secs,
ROUND(AVG(verify_duration_secs), 1) AS avg_verify_secs,
COUNT(*) AS runs
FROM backup_runs
GROUP BY backup_definition, backup_type
ORDER BY backup_definition, backup_type;Datasette supports a metadata.yml file that adds titles, descriptions, and
canned queries so the same useful views are always one click away:
title: dar-backup metrics
description: Backup run history
databases:
metrics:
tables:
backup_runs:
description: One row per backup run
queries:
failed_files:
title: Runs with filesystem errors
sql: >
SELECT run_started_at, backup_definition, backup_type,
inodes_failed, inodes_total, dar_exit_code
FROM backup_runs
WHERE inodes_failed > 0
ORDER BY run_started_at DESC
recent_runs:
title: Last 20 runs
sql: >
SELECT run_started_at, backup_definition, backup_type,
status, duration_secs, inodes_saved, inodes_total
FROM backup_runs
ORDER BY run_started_at DESC
LIMIT 20Start datasette with the configuration file:
datasette ~/dar-backup/dar-backup-metrics.db --metadata metadata.yml

