How it works¶
The exporter has two inputs and one output. The output is /metrics on port 9750, which Prometheus scrapes.
The inputs are a Duplicacy CLI log, parsed line by line, or the JSON report the Duplicacy Web UI posts to
report_url when a backup ends.
flowchart LR
CLI[Duplicacy CLI<br/>log file or container logs] -->|log_tail: parse each line| EXP[duplicacy-exporter]
WEB[Duplicacy Web UI<br/>report_url] -->|webhook: one POST per backup| EXP
EXP -->|/metrics| PROM[Prometheus]
EXP --> STATE[(STATE_FILE)]
EXP -.->|poller, optional| STORE[(duplicacy list, check)]
Log tail mode¶
MODE=log_tail reads either a file (LOG_FILE) or a container's log stream through the Docker socket
(DOCKER_CONTAINER_NAME). The parser tracks one backup or prune at a time:
- A section header,
--- Backup -> Primary (<snapshot id>) ---or--- Prune Primary ---, opens a run and sets the snapshot id.DUPLICACY_META snapshot_id=... machine=...lines, written by duplicacy-cli-cron, set the labels directly. Storage set to <url>sets the storage target label (the host part of the URL, mapped throughSTORAGE_HOST_MAPandTAILSCALE_DOMAIN) and turnsduplicacy_backup_runningon.- Every
Uploaded chunkorSkipped chunkline updates progress, speed and the chunk counters. Backup for <path> at revision <n> completedcloses the run: exit code 0, revision, success timestamp. TheFiles:,All chunks:andTotal running time:lines that follow fill the summary series. A line containingBackup failedcloses it with exit code 1.- In a prune section,
All fossil collections have been removed(orno snapshot to delete,nothing to prune) sets the prune success timestamp.
A plain duplicacy backup log has no section header, so nothing opens a run: the summary lines still
resolve when SNAPSHOT_ID and MACHINE_NAME are set, but the live series stay at zero.
With the Docker socket, the exporter replays the last REPLAY_HOURS of log on start and remembers the last
line's timestamp in TIMESTAMP_FILE, so a restart does not count a run twice. A run that ended more than
REPLAY_HOURS before the restart is outside the replay and is not read again; the saved summary of the last
completed run still serves its values.
Webhook mode¶
MODE=webhook accepts the Web UI's report on WEBHOOK_PATH (/webhook). The report is one flat JSON object
sent only when a backup ends, so there are no live values. The snapshot id is the last path component of the
report's directory (the report has no id field), the machine is computer, and the storage target is the
host of storage. The fields are on Webhook payload.
What is kept across restarts¶
The last completed values (the duplicacy_backup_last_*, prune, poller and counter series) are written to
STATE_FILE every PERSIST_INTERVAL seconds when they change, and restored before the first scrape after a
restart. The live series (running, progress, speed, the chunk counters) are not, because they describe
a run that is no longer in flight. See Persistence across restarts.
The image¶
drumsergio/duplicacy-exporter is python:3.14-alpine plus prometheus_client and the duplicacy CLI 3.2.5
binary, which only the optional storage poller uses. It is about 39 MB compressed, for
amd64 and arm64, and runs as root by default (the Docker socket needs it; set user: if you tail a file).