BackupLint
Find Docker data your backups missed before you discover it during a restore.
Don’t just assume your backups work. Prove they work.
BackupLint is an independent backup-assurance layer for Docker Compose stacks. It compares Compose mounts with your backup configuration and reports PASS, WARN, FAIL, or operational ERROR.
It is not a backup engine. It does not replace Restic, Borg, Kopia, or your backup jobs. For v1, Restic is the only supported engine when engine-backed checks are enabled. It does not SSH into hosts, delete data, or restore over live application files.
coverage ≠ integrity ≠ restore verification ≠ disaster recovery
presence (online / stale / offline) ≠ audit health
coverage FAIL ≠ repository-unavailable ERROR
Choose your deployment
BackupLint is one package. Pick a role:
| Role | Use it when | What it does |
|---|---|---|
| Standalone | One machine, local backup assurance only | Audits this machine. No fleet controller/dashboard and does not receive other agents. |
| Agent | This machine reports to an existing BackupLint controller | Audits locally and reports over mTLS. No controller/dashboard on this machine. |
| Controller | Dedicated central management node | Receives agent reports, hosts the optional dashboard, central policy, fleet state, and optional SIEM export. |
| All-in-one | The main server should audit itself and manage other agents | Runs local checks and the fleet controller/dashboard on the same machine. |
One server only → Standalone
Dedicated management server → Controller
Additional monitored servers → Agent
Main server + fleet management on one machine → All-in-one
Standalone vs all-in-one: standalone never runs a controller. All-in-one does: it is a controller that also audits the host it runs on. Use standalone unless you need a fleet.
Details: docs/installation.md, docs/installation-profiles.md.
60-second local check (standalone)
- Install the release wheel (see Native install).
- Create
backuplint.ymlnext to your Compose file:
backup_paths:
- /srv/docker
- /srv/app-config
- Scan:
backuplint scan compose.yml
backuplint scan compose.yml --config backuplint.yml --json
BackupLint Audit
✓ sonarr /srv/docker/sonarr protected
✓ radarr /srv/docker/radarr protected
✗ vaultwarden /srv/vaultwarden not protected
Result: FAIL
Want fleet management?
Use controller on a management node (or all-in-one on the main server), then agent on every other host. See Choose your deployment, docs/fleet.md, and docs/dashboard.md.
What BackupLint needs from you
BackupLint does not require one broad privileged “admin account”. You supply only what each role needs.
Local audit (standalone, agent, or all-in-one)
- Compose file path
- BackupLint config (
backuplint.yml) - Expected backup paths
- Docker Engine + Compose plugin access when Compose discovery is used
- Restic repository location for Restic-backed checks
- Restic password via
password_file,RESTIC_PASSWORD, orRESTIC_PASSWORD_FILE(password_filealways wins). Passwords are never printed.
Controller
- Hostname/DNS name agents will use (certificate SAN)
- Listener address/port (typically
8443) - Controller state directory
- Dashboard operator password if the dashboard is enabled
- Optional SIEM endpoint/config
Agent
- Controller URL
- Unique agent ID from
enroll-token - One-time enrollment token
- Controller CA certificate
- Local Compose/config/Restic access required for the checks that agent will run
Enrollment:
- the agent generates its private key locally
- the controller signs a CSR
- the agent private key is not copied to the controller
- the enrollment token is one-use
- after enrollment, traffic is mTLS
Revoke with backuplint controller revoke <agent_id>.
Native / bare-metal install
Python 3.11+ (venv). Rocky/Alma 9: use python3.11. Docker Engine + Compose plugin for scan. Restic on PATH only if you enable engine-backed checks.
Download backuplint-1.0.0-py3-none-any.whl from the v1.0.0 GitHub Release, then:
python3 -m venv .venv
source .venv/bin/activate
pip install backuplint-1.0.0-py3-none-any.whl
backuplint --version
Interactive native installer (directories, optional packages, systemd):
sudo backuplint install --interactive --yes
Unattended profile:
sudo backuplint install --profile profile.yaml --non-interactive --yes
Roles, layout, CSR enrollment, upgrades, uninstall: docs/installation.md, docs/native-installer.md.
Docker install
Official GHCR images are published for linux/amd64. Raspberry Pi 4 remains a native-install current-candidate platform.
docker pull ghcr.io/netryon/backuplint-controller:1.0.0
docker pull ghcr.io/netryon/backuplint-agent:1.0.0
The same images are also tagged v1.0.0 and latest (stable 1.0.0 convenience tag). Prefer digest pinning in production; immutable digests are listed on the v1.0.0 GitHub Release.
Controller and agent images run as uid 10001. Privileged mode is not required. A Docker socket is not required for heartbeat, enrollment, or policy. Compose discovery inside an agent container needs intentional host/Docker access (not the default).
Local source builds remain available as an advanced option:
docker build -f Dockerfile.controller --build-arg BACKUPLINT_VERSION=1.0.0 -t backuplint-controller:1.0.0 .
docker build -f Dockerfile.agent --build-arg BACKUPLINT_VERSION=1.0.0 -t backuplint-agent:1.0.0 .
Dashboard
Optional read-only UI on the controller (https://…:8443/dashboard/). Presence and audit health stay separate. The dashboard does not write policy.
v1 authentication: a shared operator password, stored as a scrypt hash. There is no multi-user RBAC and no SSO in v1.
backuplint controller dashboard-password --data-dir ./bl-controller
backuplint controller run --data-dir ./bl-controller --listen 127.0.0.1:8443 --dashboard
Screenshots
Public-safe demo fleet (synthetic labels).
Supported platforms (v1.0.0)
| Platform | Arch | Current-candidate |
|---|---|---|
| Ubuntu | x86_64 | tested |
| Debian | x86_64 | tested |
| Rocky Linux 9 | x86_64 | tested |
| Fedora 43 | x86_64 | tested |
| Raspberry Pi 4 / Raspberry Pi OS | ARM64 | tested |
| Native controller + agent | — | tested |
| Container controller + agent, including mixed topologies | — | tested |
Raspberry Pi 3 is not a current-candidate proof. WSL2 is unsupported.
Controller sizing is evidence-backed through 500 agents (about 6–8 MB/agent/day management-plane traffic at the tested 30s cadence). That excludes Restic backup payload. Do not treat >500 agents as certified capacity.
docs/support-matrix.md · docs/validation.md · docs/architecture.md
Security
Read-only diagnostics. No deletion of user data, no modification of backups, no secret printing from Compose, .env, or backup tools.
Container images: digest-pinned python:3.12-slim-bookworm, Trivy-reviewed, residual CVEs remain. Not a “zero CVE” claim. Agent: non-root uid 10001, unprivileged, no baked secrets.
docs/security.md · SECURITY.md
Known limitations
- Named volume coverage depends on local
docker volume inspect; missing volumes are WARN, not FAIL - Cache/temporary mounts are skipped by path heuristics
- Restic coverage uses snapshot path roots, not file-level contents
- Database image warnings are advisory; filesystem coverage is not a consistent DB backup
- Isolated restore verification is not a disaster-recovery drill
- Unit tests alone are not production readiness
Documentation
| Topic | Doc |
|---|---|
| Native install | docs/installation.md |
| Native installer / systemd | docs/native-installer.md |
| Docker | docs/docker.md |
| Fleet / enrollment | docs/fleet.md |
| Dashboard | docs/dashboard.md |
| Troubleshooting | docs/troubleshooting.md |
| Validation evidence | docs/validation.md |
| Release notes | docs/release-notes-v1.0.0.md |
Feedback, bugs, and questions
Feedback is welcome. Please use the channel that matches what you need:
- Bug report: open a bug report
- Feature request / improvement idea: request a feature
- Setup / usage question: ask a question
- Security vulnerability: use a private GitHub Security Advisory — do not post secrets or vulnerability details in a public issue.
Before posting logs or configuration, remove passwords, tokens, private keys, internal hostnames/IPs, repository credentials, and personal data.
Development install
For contributors only (editable tree + test extras):
git clone https://github.com/Netryon/BackupLint.git
cd BackupLint
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
ruff check src tests
pytest -q tests/unit
See CONTRIBUTING.md.
License
MIT © 2026 BackupLint contributors.
Metadata
Release files for backuplint 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| backuplint-1.0.0.tar.gz | 213.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| backuplint-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 458.3 kB
Release files / backuplint-1.0.0.tar.gz
| Download URL | backuplint-1.0.0.tar.gz |
|---|---|
| Size | 213.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5c14a48e41f47546d5f8fe514562ea944cf0855d47843cd5bae5d1f5d4a5c218
|
|
BLAKE2b-256 checksum How to use checksums |
15ff6514ef9ca521f8b5f7e2e7fc55852b18f4d379d611641848641651f74b0c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.
Transparency logRelease files / backuplint-1.0.0-py3-none-any.whl
| Download URL | backuplint-1.0.0-py3-none-any.whl |
|---|---|
| Size | 245.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9ea11b5e3821bdf04ba262fde0678e2feb2532fdf1378b4ade722483fc03afb2
|
|
BLAKE2b-256 checksum How to use checksums |
1bb3b80d7879129ecd0d837274f86df1e9aa2063ac0f2909a628a6ff6b6d273e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.
Transparency log