Files
argento/README.md
T

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: nextcloud mirror, threeteras raidz2)
  • 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.sh manually 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 -d will 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:

  1. Create docker/<app>/compose.yaml with container_name and shared network
  2. docker compose up -d
  3. ./scripts/issue-cert.sh <domain>
  4. cp docker/nginx/conf.d/_template.conf.example docker/nginx/conf.d/<domain>.conf and fill in placeholders
  5. docker exec nginx nginx -t && docker exec nginx nginx -s reload
  6. 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