Initial commit: PFI fleet inventory, stacks, tooling, and backup pipeline
Captures the full workspace state built up to this point:
- CLAUDE.md + README.md describing conventions and the four-host fleet
(ana-ml2, ana-docker, nh3-docker, esh-docker-vm).
- Per-host notes under servers/<host>/ with ssh-target fallback files
and latest system-details snapshots (two in-compose credential leaks
scrubbed; the upstream compose files still need to move those to .env).
- scripts/: server_inspect.sh (read-only remote diagnostic),
refresh-server-info.sh (dir-driven discovery + snapshot capture with
validation warnings), add-host.sh, sync-stacks.sh (pull
compose/conf trees), deploy-stack.sh (push with per-file diff + prompt).
- stacks/: canonical compose for backrest, beszel, dozzle, llama-swap,
rest-server-ana, rest-server-nh3, vllm-qwen3, plus the retired
infinity reference. All use the .env-driven + traefik-net + homepage
label pattern.
- configs/restic/ana-docker/: first resticprofile config + pre-backup
hook (Synapse pg_dump, Seafile mysqldump, Vaultwarden SQLite); templates
for the other three hosts to come.
- docs/pfi/: general infrastructure reference carried over.
- .gitignore excludes .env, stacks-mirror/, and assorted secret/state
filenames to prevent re-leaks on later commits.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# backrest stack tunables. Copy to `.env` on ana-docker before deploying.
|
||||
#
|
||||
# cp .env.example .env
|
||||
# # edit if needed
|
||||
# docker compose up -d
|
||||
#
|
||||
# Repos, S3 endpoints, restic passwords, and schedules are all configured
|
||||
# inside the Backrest web UI after first boot — nothing belongs here.
|
||||
|
||||
# Image version — pin for reproducibility (`latest` for edge)
|
||||
BACKREST_VERSION=latest
|
||||
|
||||
# Host port for the web UI (container listens on 9898 internally)
|
||||
BACKREST_PORT=9898
|
||||
|
||||
# Timezone — affects scheduler display and log timestamps
|
||||
TZ=America/Los_Angeles
|
||||
@@ -0,0 +1,65 @@
|
||||
# backrest
|
||||
|
||||
Web UI over restic repositories. Runs **once, on ana-docker**, and points at every per-host restic repo on both site-local S3 endpoints to give a single pane of glass for snapshot history, restores, and alerts.
|
||||
|
||||
**Server:** ana-docker
|
||||
**Port:** `http://10.250.50.70:9898`
|
||||
|
||||
## Role in the fleet
|
||||
|
||||
- Actual backups are run by per-host `systemd` timers calling `restic` (see the backup design; not yet deployed). Each host writes to its site-local S3 bucket (TrueNAS on ana, Synology on nh3).
|
||||
- **This container does not run backups by default** — it's a viewer/manager pointed at existing repos. (Backrest *can* be the scheduler instead of systemd timers; we're keeping the scheduler on the host for simplicity.)
|
||||
- Because it only needs to talk to S3 endpoints (not host filesystems), no bind mounts of host paths are required.
|
||||
|
||||
## Deploy
|
||||
|
||||
```bash
|
||||
# On ana-docker:
|
||||
sudo mkdir -p /opt/docker/compose/backrest
|
||||
sudo chown $USER /opt/docker/compose/backrest
|
||||
cd /opt/docker/compose/backrest
|
||||
|
||||
# scp compose.yaml + .env.example from this workspace, then:
|
||||
cp .env.example .env
|
||||
# edit .env if you want a different port or TZ
|
||||
|
||||
docker compose config
|
||||
docker compose up -d
|
||||
docker compose logs -f
|
||||
```
|
||||
|
||||
Open `http://10.250.50.70:9898` and set the admin credentials on first load.
|
||||
|
||||
## First-time configuration (in the UI)
|
||||
|
||||
For each per-host repo (to be added once the restic pipeline is running):
|
||||
|
||||
1. **Add Repository** → fill in:
|
||||
- **ID:** e.g. `ana-docker`, `ana-ml2`, `nh3-docker`, `esh-docker-vm`
|
||||
- **URI:** `s3:https://10.250.50.50:4521/pfi-backups/ana/ana-docker/` (or the nh3 endpoint, depending on host)
|
||||
- **Password:** the restic passphrase for that host
|
||||
- **Env:** `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` for the S3 endpoint
|
||||
2. *(Do not create a plan unless you want Backrest to drive the schedule — leave schedules to the systemd timers on each host.)*
|
||||
3. Verify: the repo should list snapshots pulled by the host's systemd timer within a few minutes.
|
||||
|
||||
## Backup scope, when wired up
|
||||
|
||||
- ana-docker, ana-ml2 → TrueNAS S3 at `10.250.50.50:4521`
|
||||
- nh3-docker, esh-docker-vm → Synology S3 at `10.100.50.50:4521`
|
||||
- Cross-site rclone sync makes each bucket also hold the other site's data, so you can restore *either* host from *either* side if one NAS is down.
|
||||
|
||||
## Scaling knobs
|
||||
|
||||
- **Upgrade:** `docker compose pull && docker compose up -d`.
|
||||
- **Backup of Backrest itself:** its config/DB lives in the `backrest_config` and `backrest_data` volumes — include those in ana-docker's restic plan so recreating the UI doesn't mean re-entering every repo.
|
||||
|
||||
## Why not `offen/docker-volume-backup`?
|
||||
|
||||
Two instances on `esh-docker-vm` (paperless-ngx, pgadmin) currently use the `offen/docker-volume-backup` sidecar pattern. Those will be retired once restic is in place:
|
||||
|
||||
- No encryption — backups sit in the clear on NFS.
|
||||
- No dedup — every run writes a full tarball; storage grows linearly.
|
||||
- Per-stack config — every new service needs its own sidecar wiring.
|
||||
- No cross-host index — restores require knowing which tarball lives where.
|
||||
|
||||
Restic + Backrest solves all four at the cost of one extra binary per host. Leave the sidecars running until the restic plan is verified, then remove them in a scheduled change.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Backrest — web UI over restic repositories.
|
||||
#
|
||||
# Role here: single central viewer for every host's restic repo on both
|
||||
# site-local S3 endpoints (TrueNAS at ana, Synology at nh3). Per-host
|
||||
# `restic` runs will still be driven by systemd timers on each host; this
|
||||
# stack is how we see what ran, browse snapshots, and restore.
|
||||
#
|
||||
# Repos and S3 credentials are configured in the Backrest UI after first
|
||||
# boot — nothing baked into this file. Data (its own SQLite + queue) lives
|
||||
# in a named volume so the config survives container recreation.
|
||||
#
|
||||
# All tunables live in .env — edit that, not this file.
|
||||
|
||||
services:
|
||||
backrest:
|
||||
image: garethgeorge/backrest:${BACKREST_VERSION}
|
||||
container_name: backrest
|
||||
hostname: backrest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${BACKREST_PORT}:9898"
|
||||
volumes:
|
||||
- backrest_data:/data
|
||||
- backrest_config:/config
|
||||
- backrest_cache:/cache
|
||||
- backrest_tmp:/tmp
|
||||
environment:
|
||||
- BACKREST_DATA=/data
|
||||
- BACKREST_CONFIG=/config/config.json
|
||||
- XDG_CACHE_HOME=/cache
|
||||
- TMPDIR=/tmp
|
||||
- TZ=${TZ:-America/Los_Angeles}
|
||||
- BACKREST_PORT=0.0.0.0:9898
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://localhost:9898/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
networks:
|
||||
- tnet
|
||||
labels:
|
||||
- homepage.group=PFI-ANA
|
||||
- homepage.name=Backrest
|
||||
- homepage.icon=mdi-backup-restore
|
||||
- homepage.description=Restic snapshot viewer / restore UI
|
||||
- homepage.href=http://10.250.50.70:${BACKREST_PORT}
|
||||
|
||||
volumes:
|
||||
backrest_data:
|
||||
backrest_config:
|
||||
backrest_cache:
|
||||
backrest_tmp:
|
||||
|
||||
networks:
|
||||
tnet:
|
||||
name: traefik-net
|
||||
external: true
|
||||
Reference in New Issue
Block a user