Files
docs-alpha.jayfield.org/scripts/clients/vlda-01
jensandClaude Sonnet 5 5b286644db Unmount WSD2L840 after sync, but only if this run mounted it
Previously the pull script mounted the Unassigned Device and left it
mounted indefinitely. Now tracks whether it was already mounted before
this run and only unmounts at the end if it wasn't - never disturbs a
mount already in place for some other reason. Handled via an EXIT trap
so it fires on any exit path, not just success.

Found and fixed a real bug testing this: the script cd's into the
mounted directory and never leaves, so the unmount was failing with
"target is busy" (the shell's own cwd still being on the device) until
the cleanup function explicitly cd's out first. Verified both
directions live: already-mounted stays mounted after; not-mounted gets
mounted, used, and cleanly unmounted again.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 17:33:58 +02:00
..

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 /usr are RAM-backed (rootfs / an overlay on 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.
  • /boot is VFAT, which has no concept of a Unix execute bit at all. A script stored there can never be chmod +x'd in any way that sticks — don't try to exec it directly from another script; invoke it via bash <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 and chmod +xs that copy before running it — but a script invoking another script directly, as the -onboot wrapper 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; see gitea-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_cron folds the JSON's cron-style entries into the live /etc/cron.d/root at every boot; "frequency": "boot" entries instead run once via the plugin's own disks_mounted array-start event hook, not through cron at all.
  • SSH keys under /root/.ssh needed 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, and unmounts it again afterward — but only if this run is the one that mounted it, never disturbing a mount it found already in place. 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.