Modular Docker-based backup system for self-hosted services, with a web GUI.
cp .env.example .env
chmod 600 .env
# Fill in .env with your real values
chmod 700 backups logs state
docker compose build
docker compose up -d
docker logs -f service-backup-agent
Open the GUI at http://localhost:8080
Each service has a worker that talks to that service's own REST/GraphQL
API (or, for Vaultwarden, its official CLI) — never the underlying database
directly. There are no database dumps, no docker exec into other
containers, and no DB credentials anywhere in this project. A worker only
ever gets what that service's own API is willing to hand back to an
authenticated client, written out as JSON/Markdown and archived into a
tar.gz (or left as a single JSON file for small exports).
A scheduler runs each enabled worker on a cron-like schedule, writes the
result to state/{service}/last_result.json, and optionally uploads the
output to a configured destination (currently Google Drive).
See architecture.md for the full data flow, API surface,
and design decisions.
| Service | Backs Up | Method | Setup |
|---|---|---|---|
| Vaultwarden | Full vault (logins, notes, cards, identities) | bw CLI → encrypted JSON | guide |
| Wiki.js | All pages, content + metadata | GraphQL API | guide |
| Snipe-IT | Assets, licenses, accessories, users, locations, custom fields | REST API | guide |
| Bar Assistant | Cocktails, ingredients, glasses, tags, collections (per bar) | REST API | guide |
| KitchenOwl | Households, recipes, items, shopping lists | REST API | guide |
| Linkwarden | Links + collections (full migration export) | REST API | guide |
| n8n | Workflows, tags, variables | REST API | guide |
| Karakeep | Bookmarks (with content), lists, tags, highlights | REST API | guide |
| Spoolman | Spools (incl. archived), filaments, vendors, settings | REST API | guide |
| Immich | Metadata only — albums, people, tags, EXIF (not media files) | REST API | guide |
| Nginx Proxy Manager | Hosts, streams, access lists, cert metadata, settings | REST API | guide |
| AdGuard Home | DNS settings, filters, rewrites, clients, DHCP/TLS config | REST API | guide |
Destination: Google Drive — uploads every successful backup after it's written locally.
app/workers/<name>.py inheriting BackupWorkerworker_type, display_name, description, env_var_specsrun(context: BackupContext) -> BackupResultapp/core/registry.py → create_default_registry()config/services.jsondocs/services/The GUI loads worker metadata dynamically — no GUI changes needed.
The web GUI has no built-in authentication — put it behind an auth proxy
(nginx + basic auth, Authelia, Traefik forward auth) for any remote access.
See security.md for the full threat model and secret-handling
details.
ls -lh backups/vaultwarden/
ls -lh backups/wikijs/
pip install -r requirements.txt
pytest -v
pip install ruff
ruff check app/ tests/
ruff format app/ tests/
requirements.txt is a fully pinned lock file generated by pip-tools.
requirements.in contains the human-readable version constraints.
To regenerate the lock file after updating requirements.in:
pip install pip-tools
pip-compile requirements.in -o requirements.txt
The Bitwarden CLI version is pinned via the BW_CLI_VERSION build arg in the Dockerfile.
To update it: edit the ARG BW_CLI_VERSION=... line, then rebuild.
Python
77.9%
JavaScript
12.4%
CSS
8.2%
Modular Docker-based backup system for self-hosted services, with a web GUI.
cp .env.example .env
chmod 600 .env
# Fill in .env with your real values
chmod 700 backups logs state
docker compose build
docker compose up -d
docker logs -f service-backup-agent
Open the GUI at http://localhost:8080
Each service has a worker that talks to that service's own REST/GraphQL
API (or, for Vaultwarden, its official CLI) — never the underlying database
directly. There are no database dumps, no docker exec into other
containers, and no DB credentials anywhere in this project. A worker only
ever gets what that service's own API is willing to hand back to an
authenticated client, written out as JSON/Markdown and archived into a
tar.gz (or left as a single JSON file for small exports).
A scheduler runs each enabled worker on a cron-like schedule, writes the
result to state/{service}/last_result.json, and optionally uploads the
output to a configured destination (currently Google Drive).
See architecture.md for the full data flow, API surface,
and design decisions.
| Service | Backs Up | Method | Setup |
|---|---|---|---|
| Vaultwarden | Full vault (logins, notes, cards, identities) | bw CLI → encrypted JSON | guide |
| Wiki.js | All pages, content + metadata | GraphQL API | guide |
| Snipe-IT | Assets, licenses, accessories, users, locations, custom fields | REST API | guide |
| Bar Assistant | Cocktails, ingredients, glasses, tags, collections (per bar) | REST API | guide |
| KitchenOwl | Households, recipes, items, shopping lists | REST API | guide |
| Linkwarden | Links + collections (full migration export) | REST API | guide |
| n8n | Workflows, tags, variables | REST API | guide |
| Karakeep | Bookmarks (with content), lists, tags, highlights | REST API | guide |
| Spoolman | Spools (incl. archived), filaments, vendors, settings | REST API | guide |
| Immich | Metadata only — albums, people, tags, EXIF (not media files) | REST API | guide |
| Nginx Proxy Manager | Hosts, streams, access lists, cert metadata, settings | REST API | guide |
| AdGuard Home | DNS settings, filters, rewrites, clients, DHCP/TLS config | REST API | guide |
Destination: Google Drive — uploads every successful backup after it's written locally.
app/workers/<name>.py inheriting BackupWorkerworker_type, display_name, description, env_var_specsrun(context: BackupContext) -> BackupResultapp/core/registry.py → create_default_registry()config/services.jsondocs/services/The GUI loads worker metadata dynamically — no GUI changes needed.
The web GUI has no built-in authentication — put it behind an auth proxy
(nginx + basic auth, Authelia, Traefik forward auth) for any remote access.
See security.md for the full threat model and secret-handling
details.
ls -lh backups/vaultwarden/
ls -lh backups/wikijs/
pip install -r requirements.txt
pytest -v
pip install ruff
ruff check app/ tests/
ruff format app/ tests/
requirements.txt is a fully pinned lock file generated by pip-tools.
requirements.in contains the human-readable version constraints.
To regenerate the lock file after updating requirements.in:
pip install pip-tools
pip-compile requirements.in -o requirements.txt
The Bitwarden CLI version is pinned via the BW_CLI_VERSION build arg in the Dockerfile.
To update it: edit the ARG BW_CLI_VERSION=... line, then rebuild.
Python
77.9%
JavaScript
12.4%
CSS
8.2%