5.6 KiB
argento
Configuration and recovery repo for the argento home server. This repo is the runbook -- it contains the actual config files and documents the manual steps between them.
- Rocky Linux 9 on NVMe
- ZFS storage (2 pools:
nextcloudmirror,threeterasraidz2) - Docker apps behind nginx reverse proxy
- Daily USB backup via rsync
Repo structure
scripts/ Backup, cert issuance, health monitoring, system config sync
docker/ Docker app configs (compose files, nginx confs)
nginx/ Reverse proxy + SSL termination
nextcloud/ Nextcloud + PostgreSQL
gitea/ Gitea + CI runner
jellyfin/ Media server
garage/ S3-compatible storage
minecraft/ MCSManager
immich/ Immich photo server + PostgreSQL
beszel-agent/ System monitoring agent (reports to beszel.hantim.net)
smb/ Samba share (ZFS mountpoint, not in git)
system/ System config snapshots (smb.conf, sanoid.conf, firewalld, etc.)
Key files
| File | Purpose |
|---|---|
RUNBOOK.md |
Full recovery steps, top to bottom |
scripts/backup.sh |
Daily USB backup (DB dumps + rsync) |
scripts/issue-cert.sh |
Issue SSL certs via certbot webroot |
scripts/disk-health-check.sh |
SMART, ZFS, disk space monitoring (daily alerts + weekly reports via cron) |
scripts/sync-system.sh |
Auto-sync system configs to git |
system/tracked-configs |
Maps system config paths to repo paths |
docker/nginx/conf.d/_template.conf.example |
Template for new nginx app confs |
Workflows
Compose files -- edit in /opt/argento/, apply, commit:
cd /opt/argento/docker/<app>
vim compose.yaml
docker compose up -d
git add . && git commit && git push
System configs -- edit in place, auto-synced daily:
vim /etc/samba/smb.conf
systemctl restart smb
# sync-system.sh runs via cron, or run manually: /opt/argento/scripts/sync-system.sh
Nginx configs -- edit in repo, reload:
vim /opt/argento/docker/nginx/conf.d/<domain>.conf
docker exec nginx nginx -t
docker exec nginx nginx -s reload
git add . && git commit && git push
Updating an app (Diun notification received):
Diun emails when a watched image has a new digest on the registry. The email
names the image (e.g. postgres:17-alpine), not the container. To find which
container uses it:
docker ps --filter "ancestor=<image>:<tag>" --format '{{.Names}}'
Then apply the update from that app's directory:
cd /opt/argento/docker/<app>
docker compose pull
docker compose up -d
docker ps # verify container is healthy
Then smoke-test the app (open the UI, or curl -I https://<domain>).
Caveats before pulling:
- Diun only reports that a digest changed -- it does not say the update is safe. For apps with schema migrations (Nextcloud, Immich, Gitea, Postgres), skim the release notes first.
- For major version bumps, run
./scripts/backup.shmanually before pulling so the USB has a fresh snapshot. - Postgres major version bumps (e.g. 17 -> 18) cannot reuse the old data dir -- a plain
compose up -dwill fail to start. See RUNBOOK.md. - After several updates, reclaim disk with
docker image prune -f.
Apps with a local Dockerfile:
Some compose files build their image locally instead of pulling a tagged one:
| App | Service | Base image | Why |
|---|---|---|---|
nextcloud |
nextcloud |
nextcloud:latest |
Adds smbclient + ffmpeg |
minecraft |
daemon |
githubyumao/mcsmanager-daemon:latest |
Adds Temurin 25 JRE |
For these, plain docker compose pull fails on the buildable service ("pull access denied"). Use one of:
docker compose pull --ignore-buildable # pull only registry-backed services
docker compose build --pull # rebuild, refreshing the FROM base
docker compose up -d --build # build + pull + recreate, one shot
Diun caveat: Diun watches the running container's image reference. Since these run as local images (e.g. mcsmanager-daemon-java25), the registry lookup has nothing to compare against -- Diun will not notify when the base image updates. Refresh these manually on your own cadence with docker compose build --pull && docker compose up -d.
Adding a new app:
- Create
docker/<app>/compose.yamlwithcontainer_nameandsharednetwork docker compose up -d./scripts/issue-cert.sh <domain>cp docker/nginx/conf.d/_template.conf.example docker/nginx/conf.d/<domain>.confand fill in placeholdersdocker exec nginx nginx -t && docker exec nginx nginx -s reload- Commit and push
Recovery
See RUNBOOK.md for full disaster recovery steps.
What's protected where
| Data | Protection | Recovery |
|---|---|---|
| Configs (compose, nginx, system) | Git (Gitea bare repo on USB backup) | git clone from USB |
| Nextcloud files | ZFS mirror + sanoid snapshots + USB | rsync from USB |
| Media library | ZFS raidz2 + sanoid snapshots + USB | rsync from USB |
| Nextcloud DB (PostgreSQL) | ZFS dataset + pg_dumpall + USB |
rsync from USB |
| Immich library (photos/videos) | ZFS raidz2 + sanoid snapshots + USB | rsync from USB |
| Immich DB (PostgreSQL) | pg_dumpall + USB |
rsync from USB |
| Garage S3 data | 2-node replication + USB | Replication or USB |
| Gitea repos + DB | SQLite .backup + USB |
rsync from USB |
| Secrets (.env files) | USB backup + Bitwarden | rsync from USB, or recreate from Bitwarden |
| SSL certificates | Re-issued from Let's Encrypt | ./scripts/issue-cert.sh |