2026-02-02 22:15:54 +01:00
|
|
|
# pobsync
|
|
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
`pobsync` is a pull-based backup service. It runs on a central backup server and pulls data from remote machines via rsync over SSH.
|
2026-02-02 22:15:54 +01:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
The refactor direction is SQL-first:
|
2026-02-02 22:15:54 +01:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
- Django is the management layer and source of truth.
|
|
|
|
|
- SQLite is the default database; MariaDB is optional.
|
|
|
|
|
- Backups still use the existing rsync snapshot engine internally.
|
2026-05-19 15:59:07 +02:00
|
|
|
- Scheduling is handled by a Django scheduler service, not host cron.
|
2026-05-19 05:14:29 +02:00
|
|
|
- Legacy YAML import/export exists only for migration and inspection.
|
2026-02-02 22:15:54 +01:00
|
|
|
|
|
|
|
|
## Requirements
|
|
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
On the backup server or in the container:
|
2026-02-04 01:26:01 +01:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
- Python 3.11+
|
2026-02-04 01:26:01 +01:00
|
|
|
- rsync
|
|
|
|
|
- ssh
|
|
|
|
|
- SSH key-based access from the backup server to remotes
|
2026-05-19 15:59:07 +02:00
|
|
|
- systemd for the recommended production deployment
|
2026-02-04 01:26:01 +01:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
## Local Development
|
2026-02-04 01:26:01 +01:00
|
|
|
|
|
|
|
|
```
|
2026-05-19 05:14:29 +02:00
|
|
|
python3 -m venv .venv
|
|
|
|
|
. .venv/bin/activate
|
|
|
|
|
python3 -m pip install -e .
|
|
|
|
|
mkdir -p var
|
|
|
|
|
python3 manage.py migrate
|
|
|
|
|
python3 manage.py createsuperuser
|
|
|
|
|
python3 manage.py runserver
|
2026-02-04 01:26:01 +01:00
|
|
|
```
|
|
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
The admin is available at:
|
2026-02-03 22:39:13 +01:00
|
|
|
|
2026-05-19 11:53:32 +02:00
|
|
|
- http://127.0.0.1:8000/
|
2026-05-19 05:14:29 +02:00
|
|
|
- http://127.0.0.1:8000/admin/
|
2026-02-03 22:39:13 +01:00
|
|
|
|
2026-05-19 11:43:50 +02:00
|
|
|
Staff-only JSON endpoints are available at:
|
|
|
|
|
|
|
|
|
|
- http://127.0.0.1:8000/api/
|
2026-05-19 11:46:22 +02:00
|
|
|
- http://127.0.0.1:8000/api/status/
|
2026-05-19 11:43:50 +02:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
## SQL-First Setup
|
2026-02-03 22:39:13 +01:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
Create global config:
|
2026-02-03 22:39:13 +01:00
|
|
|
|
2026-02-04 01:26:01 +01:00
|
|
|
```
|
2026-05-19 05:14:29 +02:00
|
|
|
pobsync configure-global --backup-root /mnt/backups/pobsync
|
2026-02-04 01:26:01 +01:00
|
|
|
```
|
2026-02-03 22:39:13 +01:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
Create a host config:
|
2026-05-19 04:48:13 +02:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
```
|
|
|
|
|
pobsync configure-host <host> --address <host-or-ip>
|
|
|
|
|
```
|
2026-05-19 04:48:13 +02:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
Run a backup:
|
2026-05-19 04:48:13 +02:00
|
|
|
|
|
|
|
|
```
|
2026-05-19 05:14:29 +02:00
|
|
|
pobsync backup <host> --prune
|
2026-05-19 04:48:13 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
Create or update a schedule:
|
2026-05-19 04:48:13 +02:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
```
|
|
|
|
|
pobsync schedule <host> --cron "15 2 * * *" --prune
|
|
|
|
|
```
|
2026-05-19 04:48:13 +02:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
Run the scheduler:
|
2026-05-19 04:48:13 +02:00
|
|
|
|
|
|
|
|
```
|
2026-05-19 05:14:29 +02:00
|
|
|
pobsync scheduler --loop --interval 60
|
2026-05-19 04:48:13 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
Plan or apply retention manually:
|
2026-05-19 05:08:37 +02:00
|
|
|
|
|
|
|
|
```
|
2026-05-19 05:14:29 +02:00
|
|
|
pobsync retention <host>
|
|
|
|
|
pobsync retention <host> --apply --yes --max-delete 10
|
2026-05-19 05:08:37 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-19 05:18:01 +02:00
|
|
|
Discover snapshots already present on disk:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
pobsync discover-snapshots --host <host>
|
|
|
|
|
```
|
|
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
The `pobsync` executable is a thin wrapper around Django management commands. Direct Django access is also available:
|
2026-05-19 04:48:13 +02:00
|
|
|
|
|
|
|
|
```
|
2026-05-19 05:14:29 +02:00
|
|
|
pobsync django check
|
|
|
|
|
python3 manage.py run_pobsync_backup <host> --prune
|
2026-05-19 04:48:13 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
## Migration Helpers
|
2026-05-19 04:57:10 +02:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
Import existing legacy YAML configs:
|
2026-05-19 04:53:47 +02:00
|
|
|
|
|
|
|
|
```
|
2026-05-19 05:14:29 +02:00
|
|
|
python3 manage.py import_pobsync_configs --prefix /opt/pobsync
|
2026-05-19 04:53:47 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
Export SQL config to legacy runtime YAML for inspection or one-off compatibility:
|
2026-05-19 04:53:47 +02:00
|
|
|
|
|
|
|
|
```
|
2026-05-19 05:14:29 +02:00
|
|
|
python3 manage.py export_pobsync_configs --prefix /opt/pobsync
|
2026-05-19 04:53:47 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
These commands are migration helpers, not the normal operating model.
|
|
|
|
|
|
2026-05-19 15:59:07 +02:00
|
|
|
## Production With Systemd
|
|
|
|
|
|
|
|
|
|
The recommended production deployment is native systemd services on the backup server. This avoids Docker friction around
|
|
|
|
|
SSH, filesystems, large backup mounts, and host-level service logs.
|
|
|
|
|
|
|
|
|
|
Recommended layout:
|
2026-05-19 04:48:13 +02:00
|
|
|
|
|
|
|
|
```
|
2026-05-19 15:59:07 +02:00
|
|
|
/opt/pobsync/app # git checkout
|
|
|
|
|
/opt/pobsync/venv # Python virtualenv
|
|
|
|
|
/etc/pobsync/pobsync.env # settings and secrets
|
|
|
|
|
/var/lib/pobsync # SQLite database, state, runtime SSH key files, static files
|
|
|
|
|
/backups # backup storage, or set POBSYNC_BACKUP_ROOT to another absolute path
|
2026-05-19 04:48:13 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-19 15:59:07 +02:00
|
|
|
Install OS packages first:
|
2026-05-19 04:48:13 +02:00
|
|
|
|
2026-05-19 15:59:07 +02:00
|
|
|
```
|
|
|
|
|
apt install python3 python3-venv rsync openssh-client
|
|
|
|
|
```
|
2026-05-19 04:48:13 +02:00
|
|
|
|
(feature) Add full native installer and self-check page
Expand the systemd installer so it can perform a complete native
installation with sensible defaults: copy the checkout into the target
app directory, create runtime directories, write the environment file,
install dependencies, configure systemd units, and optionally configure
nginx.
Add a staff-only Django self-check page that verifies runtime settings,
required binaries, writable paths, database connectivity, global config
state, and systemd service status when available.
Document installer overrides and expose the self-check from the main
navigation.
2026-05-19 16:05:03 +02:00
|
|
|
From a checked-out copy of this repository, run:
|
2026-05-19 04:53:47 +02:00
|
|
|
|
|
|
|
|
```
|
2026-05-19 15:59:07 +02:00
|
|
|
sudo scripts/install-systemd
|
2026-05-19 04:53:47 +02:00
|
|
|
```
|
|
|
|
|
|
(feature) Add full native installer and self-check page
Expand the systemd installer so it can perform a complete native
installation with sensible defaults: copy the checkout into the target
app directory, create runtime directories, write the environment file,
install dependencies, configure systemd units, and optionally configure
nginx.
Add a staff-only Django self-check page that verifies runtime settings,
required binaries, writable paths, database connectivity, global config
state, and systemd service status when available.
Document installer overrides and expose the self-check from the main
navigation.
2026-05-19 16:05:03 +02:00
|
|
|
By default the installer copies the checkout to `/opt/pobsync/app`, creates `/opt/pobsync/venv`, writes
|
|
|
|
|
`/etc/pobsync/pobsync.env`, creates `/var/lib/pobsync` and `/backups`, installs dependencies, runs migrations, collects
|
|
|
|
|
static files, and starts the services.
|
|
|
|
|
|
|
|
|
|
Common overrides:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
sudo scripts/install-systemd \
|
|
|
|
|
--app-dir /opt/pobsync/app \
|
|
|
|
|
--backup-root /mnt/backups/pobsync \
|
|
|
|
|
--allowed-hosts backup.example.com,localhost,127.0.0.1 \
|
|
|
|
|
--csrf-trusted-origins https://backup.example.com
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Use `--force-env` when you intentionally want the installer to rewrite an existing `/etc/pobsync/pobsync.env`.
|
|
|
|
|
|
|
|
|
|
The installer creates or updates:
|
2026-05-19 15:59:07 +02:00
|
|
|
|
|
|
|
|
- `pobsync-web.service` for Gunicorn on `127.0.0.1:8010`
|
|
|
|
|
- `pobsync-worker.service` for queued backup runs
|
|
|
|
|
- `pobsync-scheduler.service` for SQL-backed schedules
|
|
|
|
|
- `/etc/pobsync/pobsync.env` if it does not exist
|
|
|
|
|
|
|
|
|
|
Edit `/etc/pobsync/pobsync.env` before exposing the service:
|
2026-05-19 12:48:32 +02:00
|
|
|
|
|
|
|
|
```
|
2026-05-19 15:59:07 +02:00
|
|
|
POBSYNC_DJANGO_ALLOWED_HOSTS=backup.example.com,localhost,127.0.0.1
|
|
|
|
|
POBSYNC_DJANGO_CSRF_TRUSTED_ORIGINS=https://backup.example.com
|
|
|
|
|
POBSYNC_BACKUP_ROOT=/backups
|
|
|
|
|
POBSYNC_WEB_BIND=127.0.0.1:8010
|
2026-05-19 12:48:32 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-19 15:59:07 +02:00
|
|
|
Restart after changes:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
sudo systemctl restart pobsync-web pobsync-worker pobsync-scheduler
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Check service state and logs:
|
2026-05-19 05:14:29 +02:00
|
|
|
|
2026-05-19 15:59:07 +02:00
|
|
|
```
|
|
|
|
|
systemctl status pobsync-web pobsync-worker pobsync-scheduler
|
|
|
|
|
journalctl -u pobsync-worker -f
|
|
|
|
|
```
|
2026-05-19 15:33:09 +02:00
|
|
|
|
(feature) Add full native installer and self-check page
Expand the systemd installer so it can perform a complete native
installation with sensible defaults: copy the checkout into the target
app directory, create runtime directories, write the environment file,
install dependencies, configure systemd units, and optionally configure
nginx.
Add a staff-only Django self-check page that verifies runtime settings,
required binaries, writable paths, database connectivity, global config
state, and systemd service status when available.
Document installer overrides and expose the self-check from the main
navigation.
2026-05-19 16:05:03 +02:00
|
|
|
The Django UI also has a staff-only `/self-check/` page that verifies runtime settings, required binaries, writable
|
|
|
|
|
paths, database connectivity, global config state, and systemd service state when systemd is available.
|
|
|
|
|
|
2026-05-19 15:59:07 +02:00
|
|
|
Update an existing native install:
|
2026-05-19 15:33:09 +02:00
|
|
|
|
2026-05-19 15:59:07 +02:00
|
|
|
```
|
|
|
|
|
git pull
|
(feature) Add full native installer and self-check page
Expand the systemd installer so it can perform a complete native
installation with sensible defaults: copy the checkout into the target
app directory, create runtime directories, write the environment file,
install dependencies, configure systemd units, and optionally configure
nginx.
Add a staff-only Django self-check page that verifies runtime settings,
required binaries, writable paths, database connectivity, global config
state, and systemd service status when available.
Document installer overrides and expose the self-check from the main
navigation.
2026-05-19 16:05:03 +02:00
|
|
|
sudo scripts/install-systemd --app-dir /opt/pobsync/app
|
2026-05-19 15:59:07 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Use an existing reverse proxy by forwarding to `http://127.0.0.1:8010`. To install a simple nginx site file as a
|
|
|
|
|
starting point:
|
2026-05-19 15:33:09 +02:00
|
|
|
|
|
|
|
|
```
|
2026-05-19 15:59:07 +02:00
|
|
|
sudo scripts/install-systemd --with-nginx --server-name backup.example.com
|
2026-05-19 15:33:09 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-19 15:59:07 +02:00
|
|
|
## Docker With SQLite
|
|
|
|
|
|
|
|
|
|
Docker Compose is still useful for local development and disposable test installs. Native systemd is preferred for
|
|
|
|
|
production backup servers.
|
2026-05-19 15:33:09 +02:00
|
|
|
|
|
|
|
|
```
|
2026-05-19 15:59:07 +02:00
|
|
|
docker compose up --build web
|
2026-05-19 15:33:09 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-19 15:59:07 +02:00
|
|
|
This starts Django on:
|
|
|
|
|
|
|
|
|
|
- http://127.0.0.1:8010/
|
|
|
|
|
- http://127.0.0.1:8010/admin/
|
|
|
|
|
- http://127.0.0.1:8010/api/
|
|
|
|
|
- http://127.0.0.1:8010/api/status/
|
|
|
|
|
|
|
|
|
|
Run the scheduler alongside the web admin:
|
2026-05-19 15:33:09 +02:00
|
|
|
|
|
|
|
|
```
|
2026-05-19 15:59:07 +02:00
|
|
|
docker compose up --build web scheduler worker
|
2026-05-19 15:33:09 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-19 15:59:07 +02:00
|
|
|
The web service runs Django through Gunicorn and serves static files with WhiteNoise. The container persists `/opt/pobsync`
|
|
|
|
|
and the SQLite database in Docker volumes.
|
|
|
|
|
Backup data is always available at `/backups` inside the containers. By default this uses `./backups` on the host.
|
|
|
|
|
Override the host-side mount with `POBSYNC_BACKUP_ROOT`:
|
2026-05-19 15:33:09 +02:00
|
|
|
|
|
|
|
|
```
|
2026-05-19 15:59:07 +02:00
|
|
|
POBSYNC_BACKUP_ROOT=/mnt/backups/pobsync docker compose up --build web scheduler worker
|
2026-05-19 15:33:09 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-19 15:59:07 +02:00
|
|
|
The Django setup UI keeps the backup root fixed at `/backups`; only the Docker mount decides which host directory
|
|
|
|
|
that points to.
|
2026-05-19 15:33:09 +02:00
|
|
|
|
2026-05-19 14:37:38 +02:00
|
|
|
## Django-Managed SSH Keys
|
|
|
|
|
|
|
|
|
|
SSH keys can be managed from the Django UI at `/ssh-credentials/`. Add a private key there, optionally paste
|
|
|
|
|
`known_hosts` entries, and select the credential either as the global default or as a per-host override.
|
|
|
|
|
|
2026-05-19 15:59:07 +02:00
|
|
|
When a backup starts, the worker writes the selected key to `$POBSYNC_HOME/state/ssh-credentials/<id>/identity`
|
|
|
|
|
with `0600` permissions and injects `IdentityFile` into the rsync SSH command. If `known_hosts` is configured, the
|
|
|
|
|
worker also writes a matching `known_hosts` file and injects `UserKnownHostsFile`.
|
2026-05-19 14:37:38 +02:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
## Docker With MariaDB
|
2026-05-19 04:48:13 +02:00
|
|
|
|
|
|
|
|
```
|
|
|
|
|
docker compose --profile mariadb up --build web-mariadb
|
|
|
|
|
```
|
|
|
|
|
|
2026-05-19 04:53:47 +02:00
|
|
|
With the scheduler:
|
|
|
|
|
|
|
|
|
|
```
|
2026-05-19 13:00:12 +02:00
|
|
|
docker compose --profile mariadb up --build web-mariadb scheduler-mariadb worker-mariadb
|
2026-05-19 04:53:47 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
SQLite remains the default because it is enough for a single backup server and keeps deployment simple.
|
|
|
|
|
|
|
|
|
|
## Current Architecture
|
|
|
|
|
|
|
|
|
|
The public command surface is Django-first. The old YAML/cron CLI has been retired from the `pobsync` entrypoint.
|
2026-05-19 11:19:22 +02:00
|
|
|
Discovered snapshots are stored in `SnapshotRecord`, including the base snapshot metadata and a nullable SQL link to the
|
|
|
|
|
base record when it is known.
|
2026-05-19 11:24:48 +02:00
|
|
|
The Django retention command plans from `SnapshotRecord` instead of rediscovering snapshots from the filesystem.
|
2026-05-19 11:32:32 +02:00
|
|
|
Post-backup pruning from Django also uses the SQL retention service after the completed snapshot is recorded.
|
2026-05-19 11:46:22 +02:00
|
|
|
Staff-only JSON endpoints expose service status, hosts, snapshots, and backup runs for lightweight inspection.
|
2026-05-19 11:53:32 +02:00
|
|
|
Staff-only dashboard views expose the same operational state through Django templates.
|
2026-05-19 11:56:45 +02:00
|
|
|
Host pages include a safe snapshot discovery action that records existing snapshots into SQL.
|
2026-05-19 12:00:19 +02:00
|
|
|
Host pages also include a read-only SQL retention plan view before any destructive pruning action.
|
2026-05-19 12:13:12 +02:00
|
|
|
Schedules can be created or updated from host pages using the same SQL-backed scheduler model.
|
2026-05-19 12:17:17 +02:00
|
|
|
Host config can be edited from host pages while keeping host identity stable.
|
2026-05-19 05:14:29 +02:00
|
|
|
|
|
|
|
|
The remaining internal engine code still contains reusable backup primitives:
|
2026-05-19 04:48:13 +02:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
- snapshot naming and metadata
|
|
|
|
|
- rsync command construction and execution
|
|
|
|
|
- retention planning and pruning
|
|
|
|
|
- host locking
|
2026-05-19 04:48:13 +02:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
Next refactor targets:
|
2026-05-19 04:48:13 +02:00
|
|
|
|
2026-05-19 05:14:29 +02:00
|
|
|
- Move more snapshot lifecycle details into typed domain objects.
|
|
|
|
|
- Replace remaining dictionary-shaped config at engine boundaries.
|
|
|
|
|
- Remove legacy YAML import/export once production migration no longer needs it.
|