Prometheus exporter for Duplicacy backup metrics -- real-time progress, speed, and post-run summaries for your Grafana dashboards.
duplicacy-exporter bridges Duplicacy backups and Prometheus, giving you full observability over backup operations. It works with both Duplicacy CLI (via log tailing) and Duplicacy Web UI (via webhook), exposing metrics that Prometheus scrapes and Grafana visualizes.
- Real-time metrics -- backup speed (bytes/sec), progress (0-100%), chunks uploaded/skipped, updated per chunk
- Post-run summaries -- duration, file counts, bytes uploaded, exit codes, revision numbers
- Prune tracking -- monitors prune operations with completion timestamps
- Two collection modes --
log_tailfor CLI users,webhookfor Web UI users - Smart label resolution -- automatic snapshot ID, storage target, and machine name detection from logs
- Storage host mapping -- translates IPs and Tailscale FQDNs into human-readable names
- Lightweight -- single Python file, one dependency (
prometheus_client), Alpine image (~30 MB)
+---------------------+ +----------------------+ +------------+
| | logs | | scrape | |
| Duplicacy CLI +-------->+ duplicacy-exporter +<--------+ Prometheus |
| (Docker / file) | tail | | :9750 | |
+---------------------+ +----------+-----------+ +------+-----+
| |
+---------------------+ POST | | |
| Duplicacy Web UI +-------->+ /webhook endpoint | +------v-----+
| (report_url) | | | | Grafana |
+---------------------+ +----------------------+ +------------+
Log tail mode connects to the Docker Engine API over a Unix socket (or tails a log file) and parses Duplicacy output line-by-line. It extracts chunk-level progress in real time and summary statistics at completion.
Webhook mode receives JSON payloads from Duplicacy Web UI's report_url setting, extracting the same summary metrics without needing Docker socket access.
Deploy alongside your Duplicacy CLI container:
services:
duplicacy-exporter:
image: ghcr.io/geiserx/duplicacy-exporter:0.3.3
container_name: duplicacy-exporter
restart: unless-stopped
environment:
- MODE=log_tail
- DOCKER_CONTAINER_NAME=duplicacy-cli-cron
- LISTEN_PORT=9750
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "9750:9750"services:
duplicacy-exporter:
image: ghcr.io/geiserx/duplicacy-exporter:0.3.3
container_name: duplicacy-exporter
restart: unless-stopped
environment:
- MODE=webhook
- LISTEN_PORT=9750
ports:
- "9750:9750"Then set report_url in Duplicacy Web UI to: http://duplicacy-exporter:9750/webhook
If you write Duplicacy logs to a file instead of using Docker:
services:
duplicacy-exporter:
image: ghcr.io/geiserx/duplicacy-exporter:0.3.3
container_name: duplicacy-exporter
restart: unless-stopped
environment:
- MODE=log_tail
- LOG_FILE=/logs/duplicacy.log
- LISTEN_PORT=9750
volumes:
- /path/to/duplicacy/logs:/logs:ro
ports:
- "9750:9750"All configuration is done through environment variables:
| Variable | Default | Description |
|---|---|---|
MODE |
log_tail |
Collection mode: log_tail or webhook |
DOCKER_CONTAINER_NAME |
duplicacy-cli-cron |
Container name to tail logs from (log_tail mode) |
LOG_FILE |
(empty) | Path to log file; alternative to Docker socket (log_tail mode) |
LISTEN_PORT |
9750 |
Port for the metrics and webhook HTTP server |
WEBHOOK_PATH |
/webhook |
Path for the webhook POST endpoint (webhook mode) |
MACHINE_NAME |
(empty) | Machine name label; auto-detected from logs if not set |
TAILSCALE_DOMAIN |
mango-alpha.ts.net |
Tailscale domain suffix to strip from storage URLs |
STORAGE_HOST_MAP |
(empty) | JSON object mapping hostname/IP to display name |
REPLAY_HOURS |
25 |
Hours of Docker log history to replay on startup |
LOG_LEVEL |
INFO |
Logging verbosity: DEBUG, INFO, WARNING, ERROR |
Map raw IPs or hostnames to friendly names:
STORAGE_HOST_MAP='{"192.168.10.100":"watchtower","192.168.20.5":"geiserct"}'All backup metrics carry labels: snapshot_id, storage_target, machine.
All prune metrics carry labels: storage_target, machine.
| Metric | Type | Description |
|---|---|---|
duplicacy_backup_running |
Gauge | 1 if backup is in progress, 0 otherwise |
duplicacy_backup_speed_bytes_per_second |
Gauge | Current backup speed |
duplicacy_backup_progress_ratio |
Gauge | Progress from 0.0 to 1.0 |
duplicacy_backup_chunks_uploaded |
Gauge | Chunks uploaded in current run |
duplicacy_backup_chunks_skipped |
Gauge | Chunks skipped in current run |
| Metric | Type | Description |
|---|---|---|
duplicacy_backup_last_success_timestamp_seconds |
Gauge | Unix timestamp of last successful backup |
duplicacy_backup_last_duration_seconds |
Gauge | Duration of last backup in seconds |
duplicacy_backup_last_files_total |
Gauge | Total files in last backup |
duplicacy_backup_last_files_new |
Gauge | New files in last backup |
duplicacy_backup_last_bytes_uploaded |
Gauge | Bytes uploaded in last backup |
duplicacy_backup_last_bytes_new |
Gauge | New bytes in last backup |
duplicacy_backup_last_chunks_new |
Gauge | New chunks in last backup |
duplicacy_backup_last_exit_code |
Gauge | Exit code: 0 = success, 1 = failure |
duplicacy_backup_last_revision |
Gauge | Revision number of last backup |
duplicacy_backup_bytes_uploaded_total |
Counter | Cumulative bytes uploaded across all runs |
| Metric | Type | Description |
|---|---|---|
duplicacy_prune_running |
Gauge | 1 if prune is in progress |
duplicacy_prune_last_success_timestamp_seconds |
Gauge | Unix timestamp of last successful prune |
| Path | Method | Description |
|---|---|---|
/metrics |
GET | Prometheus metrics endpoint |
/webhook |
POST | Duplicacy Web UI report endpoint |
/health |
GET | Health check (returns 200 OK) |
Add the exporter as a scrape target:
scrape_configs:
- job_name: "duplicacy"
static_configs:
- targets: ["duplicacy-exporter:9750"]
labels:
instance: "my-server"groups:
- name: duplicacy
rules:
- alert: DuplicacyBackupFailed
expr: duplicacy_backup_last_exit_code != 0
for: 5m
labels:
severity: warning
annotations:
summary: "Duplicacy backup failed for {{ $labels.snapshot_id }}"
- alert: DuplicacyBackupStale
expr: time() - duplicacy_backup_last_success_timestamp_seconds > 86400
for: 1h
labels:
severity: critical
annotations:
summary: "No successful Duplicacy backup in 24h for {{ $labels.snapshot_id }}"A ready-to-import dashboard is included in dashboard.json and published on Grafana.com (#25089).
Import it in Grafana via Dashboards → Import → Upload JSON file or use the dashboard ID 25089.
- Log tail mode: Verify the Docker socket is mounted (
/var/run/docker.sock:/var/run/docker.sock:ro) and theDOCKER_CONTAINER_NAMEmatches your Duplicacy container exactly. - Log file mode: Confirm the log file path is correct and the volume mount provides read access.
- Webhook mode: Ensure
report_urlin Duplicacy Web UI points tohttp://<exporter-host>:9750/webhook. The exporter must be reachable from the Web UI container.
- Set
LOG_LEVEL=DEBUGto see how each log line is parsed and which labels are resolved. - If storage targets show as raw IPs, use
STORAGE_HOST_MAPto map them to friendly names. - If machine name is missing, set
MACHINE_NAMEexplicitly.
The exporter process runs as root inside the container by default. If you run it as a non-root user, ensure the user has access to the Docker socket (typically group docker, GID 999 or similar).
Verify the WEBHOOK_PATH environment variable matches the path you configured in Duplicacy Web UI. The default is /webhook.
duplicacy-container-- Runtime image and Helm chart for the Kubernetes Duplicacy stackduplicacy-cli-cron-- Scripts, wrappers, and backup recipes for Duplicacy CLI- Duplicacy -- Lock-free deduplication cloud backup tool
- Prometheus -- Monitoring and alerting toolkit
- Grafana -- Observability and visualization platform
- awesome-prometheus -- Curated list of Prometheus resources
- prometheus_client (Python) -- Official Prometheus client library for Python