Every script/config built this session (sanity-check, attacker-check, the Gitea backup pipeline on both ends) now lives in this repo as reference copies, not just described in prose - previously they only existed on the live servers. Split into scripts/alpha/ (Ubuntu 24.04.4 LTS) and scripts/clients/vlda-01/ (Unraid 7.3.2), each with its own README stating the exact OS/kernel and a file-by-file map to deployed paths, since a script written for one host's conventions doesn't just work unchanged on the other - the vlda-01 README in particular documents the persistence gotchas that actually broke earlier attempts (RAM- backed root filesystem, VFAT /boot with no execute bit, Unassigned Device auto-mount). Root README.md now links directly to these files from each relevant section instead of only describing them. Explicitly not included: the dedicated SSH private key vlda-01 uses to authenticate to alpha - noted in clients/vlda-01/README.md to regenerate rather than ever commit one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Scripts running on vlda-01
OS: Unraid 7.3.2, kernel 6.18.38-Unraid. Very different
persistence model from Ubuntu/alpha — this tripped up the first version
of every script here, so it's worth stating plainly before anything else:
/and/usrare RAM-backed (rootfs/ anoverlayon top of the read-only squashfs image loaded from the boot USB stick). Anything written there — a plain crontab, a script dropped in/usr/local/sbin— is silently gone on the next reboot. Only/boot(the actual USB stick, mounted read/write as a normal filesystem) survives./bootis VFAT, which has no concept of a Unix execute bit at all. A script stored there can never bechmod +x'd in any way that sticks — don't try toexecit directly from another script; invoke it viabash <path>instead. (The User Scripts plugin itself handles this correctly when it runs a script through the plugin — it copies the content to a RAM-backed tmp location andchmod +xs that copy before running it — but a script invoking another script directly, as the-onbootwrapper here does, has to work around it manually.)- Real, persistent storage lives on the array (
/mnt/user/...) or on Unassigned Devices — extra disks attached but not part of the protected array, mounted at/mnt/disks/<label>— which is where these backups actually land. An Unassigned Device is not guaranteed to already be mounted when a script runs; seegitea-backup-pull's own handling of this below. - Scheduled tasks persist via the User Scripts plugin (already
installed, already used for this box's other maintenance), which stores
scripts under
/boot/config/plugins/user.scripts/scripts/<name>/and scheduling in/boot/config/plugins/user.scripts/schedule.json— both on the persistent USB stick.update_cronfolds the JSON's cron-style entries into the live/etc/cron.d/rootat every boot;"frequency": "boot"entries instead run once via the plugin's owndisks_mountedarray-start event hook, not through cron at all. - SSH keys under
/root/.sshneeded no special persistence handling — Unraid already auto-mirrors that whole directory to/boot/config/ssh/root/on its own.
Files, and where they're actually deployed
| File here | Deployed path | Purpose |
|---|---|---|
user-scripts/gitea-backup-pull/script |
/boot/config/plugins/user.scripts/scripts/gitea-backup-pull/script |
Pulls new gitea-dump archives from alpha via the restricted giteabackup SFTP account, verifies each one's sha256 checksum, deletes both files on alpha only after a confirmed match, prunes its own local retention (30). Resolves and mounts the WSD2L840 Unassigned Device itself first — see the script's own header comment for the full reasoning. |
user-scripts/gitea-backup-pull/description |
.../gitea-backup-pull/description |
Shown in the User Scripts UI. |
user-scripts/gitea-backup-pull-onboot/script |
.../gitea-backup-pull-onboot/script |
One-line wrapper: bash-invokes the script above. Exists only because schedule.json supports one schedule per script path — this is how the "also run once at every restart" requirement was added alongside the existing daily 03:30 schedule without disturbing it. |
user-scripts/gitea-backup-pull-onboot/description |
.../gitea-backup-pull-onboot/description |
Shown in the User Scripts UI. |
user-scripts/schedule.json |
/boot/config/plugins/user.scripts/schedule.json |
Whole file, as deployed — includes entries for this box's other, pre-existing scheduled maintenance (delete_dangling_images, rc.fancontrol, etc.) that predate this project and aren't described elsewhere in this repo. Only the gitea-backup-pull and gitea-backup-pull-onboot entries near the bottom belong to this project. |
Redeploying from this checked-in copy
# on vlda-01:
mkdir -p /boot/config/plugins/user.scripts/scripts/gitea-backup-pull
mkdir -p /boot/config/plugins/user.scripts/scripts/gitea-backup-pull-onboot
# copy script/description files into place from this repo, then:
cp /boot/config/plugins/user.scripts/schedule.json /tmp/user.scripts/schedule.json
/usr/local/sbin/update_cron
The dedicated ed25519 keypair this depends on (/root/.ssh/gitea-backup/,
authorized on alpha's giteabackup account) is not in this repo —
regenerate it and re-authorize the new public key on alpha rather than
ever committing a private key here.