Backup NAS — Cooper¶
Last Updated: 2026-07-18 Status: ✅ Active Purpose: Backup target. Receives backups from Lotus. Minimal services to reduce unnecessary disk spin-up.
Hardware specs: inventory.md → Cooper
Access¶
| Method | Detail |
|---|---|
| Web UI | http://192.168.1.60 |
| SSH | ssh root@192.168.1.60 -p 50123 (custom port, not default 22) |
Array Configuration¶
| Parity | 1× 3.6TB |
| Disk 1 | 3.6TB |
| Disk 2 | 3.6TB |
| Usable storage | ~7.2TB |
| Cache pool | 1× 512GB SSD (2.5" SATA, bay 4) |
Cooper now has an SSD cache pool, enabling Docker appdata to run off faster storage. All array data remains on spinning hard drives.
Docker Containers¶
| Container | Status | Purpose |
|---|---|---|
| unraid-simple-monitoring-api | ✅ Running | Exposes basic monitoring endpoint |
| SWAG | ✅ Running | Standby reverse proxy — mirrors Lotus SWAG config |
| immich-postgres-replica | ✅ Running | Streaming replica of Lotus Immich PostgreSQL |
SWAG — Standby Reverse Proxy¶
Cooper runs a second SWAG instance configured identically to the primary on Lotus. It acts as a hot spare reverse proxy — if Lotus goes down, a failover script automatically updates Cloudflare DNS to point to Cooper's SWAG within ~10 minutes.
| Lotus (primary) | Cooper (standby) | |
|---|---|---|
| Tailscale hostname | swag |
swag-cooper |
| Tailscale IP | 100.106.140.33 | 100.126.183.94 |
| Cloudflare DNS | *.djchome.uk points here normally |
*.djchome.uk points here on failover |
Config Sync¶
SWAG config files are rsync'd from Lotus to Cooper hourly via a Lotus User Script (swag-sync-to-cooper). This keeps nginx proxy configs and SSL certificates in sync without manual intervention.
⚠️ Tailscale State Directory Exclusion¶
The rsync job must exclude the Tailscale state directory inside SWAG's appdata:
/mnt/cache/appdata/swag/.tailscale_state/
If this directory is copied across, both SWAG instances will share the same Tailscale node ID, causing a conflict — the primary Lotus instance will be kicked off the Tailnet and will lose external connectivity.
See Lessons Learned for full details.
Note: If Cooper's SWAG ever needs to re-authenticate with Tailscale (e.g. after clearing
.tailscale_state), the browser auth flow may hang. Use a pre-generated Tailscale auth key instead: addTAILSCALE_AUTHKEY=tskey-auth-xxxxxas a container environment variable.
Automatic Failover Script¶
A script (swag-failover) runs every 5 minutes on Cooper via User Scripts. It:
- Checks whether Lotus SWAG (100.106.140.33) is reachable on HTTPS
- After 2 consecutive failures (~10 minutes of downtime), updates the Cloudflare
*.djchome.ukwildcard A record to point to Cooper (100.126.183.94) - When Lotus recovers, automatically fails back and restores the DNS record
State and logs are stored at /mnt/cache/appdata/swag-failover/:
| File | Purpose |
|---|---|
state |
primary or failover |
failures |
Consecutive failure counter |
failover.log |
Timestamped event log |
Cloudflare record details:
- Zone ID: d2e47a211146c145dc79f28a46800b4e
- Record ID: a3fe4772cf198a5ac7af9aec1d1e7f38
- The API token for DNS updates is stored inside the failover script on Cooper
Immich PostgreSQL — Streaming Replica¶
Cooper runs a read-only PostgreSQL replica of the Immich database on Lotus. This provides data protection for photos — if Lotus's database is lost or corrupted, Cooper has a continuously-updated copy.
| Container | immich-postgres-replica |
| Image | ghcr.io/immich-app/postgres:16-vectorchord0.3.0-pgvectors0.3.0 |
| Data path | /mnt/cache/appdata/immich_postgresql |
| Primary | 192.168.1.80:5452 (via socat relay on Lotus — see Primary NAS doc) |
| Replication user | replicator |
Replication config lives in /mnt/cache/appdata/immich_postgresql/postgresql.auto.conf and standby.signal. The replica connects to Lotus on port 5452 (the socat relay), not port 5432 directly.
If replication breaks¶
- Check container logs:
docker logs immich-postgres-replica --tail 50 - Verify Lotus socat relay is running:
docker ps | grep immich-pg-relayon Lotus - Check connectivity:
bash -c 'echo > /dev/tcp/192.168.1.80/5452' && echo OKfrom Cooper - If the replica has diverged too far, re-run
pg_basebackupand restart the container
Backup Role¶
Cooper is the destination for backups from Lotus:
| Source | Method | What's backed up |
|---|---|---|
| Lotus | LuckyBackup | Data shares |
| Lotus | Appdata Backup plugin | Docker appdata |
Cooper also sends its own Docker appdata to Lotus via Appdata Backup.
Planned: Local Documentation Mirror¶
Cooper is the planned host for the local docs mirror. Once configured:
docs.djchome.ukwill resolve to Cooper on the home network (via split DNS)- Cooper will sync from GitHub and serve the MkDocs site locally
- Gabriela can use the same URL whether at home or away
See TODO.md for setup tasks.
Recovery¶
See Backup NAS Recovery for restore procedures.
⚠️ Cooper does NOT restart itself after a power cut (confirmed 2026-07-29)¶
Its BIOS is not set to power on after AC restore, and Wake-on-LAN does not work either. After any power interruption Cooper stays down until somebody physically presses its power button — silently, since it is the backup NAS and nothing depends on it minute to minute.
- Note that a graceful shutdown is not the same event as a power cut: "restore on AC loss" will not bring back a machine that was cleanly powered off, even if mains is then cycled.
- Its power comes via Pw05 (
192.168.30.59, Tasmota) — see the Smart Plug Register. Any work on that plug takes Cooper down, so shut the array down gracefully first. - Safe for a long outage: Immich's Postgres on Lotus has no replication slots, so the paused replica cannot accumulate WAL and fill Lotus's cache. But there is no backup target while Cooper is down — the nightly appdata backup, the 07:00 USB flash copy and luckyBackup all fail.
- On return,
mdcmd statusreportingmdNumDisabled=1/mdNumInvalid=1is benign — it counts the empty slot 3 (DISK_NP). All real disks readDISK_OK. - Fix worth doing: enable "restore on AC power loss" in Cooper's BIOS.
Troubleshooting¶
Shares disappear — "no exportable user shares" (recurring, root cause found 2026-07-18)¶
Symptom: Cooper's Shares page shows "There are no exportable user shares." Anything mounting Cooper over SMB (e.g. Lotus's /mnt/remotes/192.168.1.60_*) goes stale and fails to remount with NT_STATUS_BAD_NETWORK_NAME. The array itself and individual disks (/mnt/disk1, /mnt/disk2, /mnt/cache) stay healthy throughout — this is not an array or driver failure, despite how it looks from the Shares page.
Root cause: the /mnt/user FUSE mount — the shfs process that pools disk1+disk2+cache into user shares — silently dies at an unpredictable point during uptime, with no corresponding kernel or hardware event logged. mount still lists /mnt/user, but any access returns Transport endpoint is not connected, breaking every user share since they all live under /mnt/user. /mnt/user0 (the same pool minus the cache drive) stayed healthy both times this happened, making the cache drive's involvement in the pool the leading suspect — but the actual trigger for the shfs crash itself is still unconfirmed.
A plain Array Stop/Start from the GUI does not fix it. Unraid's stop sequence doesn't force-unmount a wedged FUSE mount, so on the following start, mkdir /mnt/user fails (directory already exists in the broken transport state) and the new shfs process dies immediately trying to chdir into it. Historically only a full reboot cleared it, since rebooting wipes all mount state.
Faster fix without a reboot (confirmed working 2026-07-18, via SSH):
umount -l /mnt/user
/usr/libexec/unraid/shfs /mnt/user -disks 7 -o default_permissions,allow_other,noatime -o remember=330
/etc/rc.d/rc.samba restart
-disks 7 bitmask matches Cooper's current 3-disk + cache config — if the disk layout changes, confirm the correct invocation via grep 'shfs /mnt/user' /var/log/syslog (emhttpd logs the exact command it uses on every array start). Verify with df -h /mnt/user and check /var/log/syslog for no further canonicalize_connect_path or Transport endpoint entries.
To catch the actual crash trigger next time it happens: grab a diagnostics zip (Tools → Diagnostics) as soon as it's noticed, before applying the fix above — the in-RAM logs and current FUSE state are the most useful evidence and are lost once the mount is remounted or the box is rebooted.
See Lessons Learned for the full investigation.
Full recovery checklist (updated 2026-07-19 — fixing /mnt/user alone is not enough)¶
Doing the shfs fix above gets the mount healthy again, but that alone does not fully restore SMB access. Two more things commonly need fixing on top of it — check all three before assuming the fault has recurred:
/mnt/userFUSE mount — the fix above. Verify withdf -h /mnt/user.- Cooper's own Samba share export list can end up empty. Check
cat /etc/samba/smb-shares.confon Cooper — if it's 0 bytes, Samba has no shares defined at all (smbclient -L localhost -U guest -Nwill show onlyIPC$), and any client trying to mount a share getsNT_STATUS_BAD_NETWORK_NAME. This happens whenemhttpdtries to regenerate the share export list at the exact moment/mnt/userisn't mounted yet (the same race as theshfscrash itself) — e.g. during the Array Stop/Start used to fix #1. Editing a share in the GUI and clicking Done without changing a value does not force a rewrite. The reliable fix is another Array Stop → Start now that/mnt/useris already stable (no race this time) — confirmed working 2026-07-19; it briefly bouncesswagandimmich-postgres-replicaagain. Verify withcat /etc/samba/smb-shares.conf(should list[appdata],[backups],[data],[immich]) andsmbclient -L localhost -U guest -N. -
Client-side stale CIFS mounts don't self-heal. Even after #1 and #2 are fixed on Cooper, a client that had the share mounted through the outage (e.g. Lotus, at
/mnt/remotes/192.168.1.60_*) can be left with a wedged mount:mountstill lists it, it may show a different SMB dialect than a working mount (e.g.vers=3.1.1vsvers=3.0), butls/staton it returns "No such type of file or directory" andls -laon the parent showsd?????????for that entry. Unraid's Unassigned Devices "Mount" button sees the stale entry and does nothing, since as far as the OS is concerned it's already mounted. Clear it directly on the client:umount -l /mnt/remotes/192.168.1.60_<share>, then remount.The UI Mount button is not required — this is doable entirely from the CLI on Lotus (confirmed 2026-07-29):
umount -l /mnt/remotes/192.168.1.60_appdata umount -l /mnt/remotes/192.168.1.60_backups /usr/local/sbin/rc.unassigned mount "//192.168.1.60/appdata" /usr/local/sbin/rc.unassigned mount "//192.168.1.60/backups"rc.unassigned mountwith no argument fails (Fail: device not defined) — it needs the share path exactly as written in/boot/config/plugins/unassigned.devices/samba_mount.cfg. Verify withstaton each mountpoint, and check the nightly flash backup is present under.../backups/lotus/justtheusb/.
See Lessons Learned for the full investigation.
Auto-recovery watchdog (installed 2026-07-22)¶
Because the shfs crash keeps recurring and used to go unnoticed for days, Cooper now runs a User Script watchdog that self-heals it. It does not fix the underlying bug (still unknown why shfs dies), but it turns a multi-day silent outage into a self-healing blip of a few minutes, and — just as usefully — records the exact time of death so the trigger can eventually be correlated.
- Where: Settings → User Scripts →
shfs-user-watchdog(file:/boot/config/plugins/user.scripts/scripts/shfs-user-watchdog/script). Runs every 3 minutes. - What it does: checks
/mnt/useris a live, readable mountpoint; if it's wedged, logs the timestamp + evidence and runs the Layer-1 recovery (umount -l→shfsrestart →rc.samba restart). Theshfscommand isn't hard-coded — it caches the live invocation to/boot/config/shfs-user-cmd.txton every healthy run, so it auto-adapts if the disk layout changes. - Safeguards: it won't act during a legitimate array stop or boot (it first checks
/mnt/disk1is mounted), and a 15-minute cooldown stops a failed recovery from hammering Samba in a loop — instead it logs "MANUAL intervention required". - Log to check after any recurrence:
/mnt/cache/appdata/shfs-watchdog/watchdog.log— this gives the precise crash timestamp. - Scope: it fixes Cooper (layers 1 + 2 above). It does not touch Lotus's client-side stale mounts (layer 3) — if Lotus still can't see the shares after the watchdog has healed Cooper, clear them manually on Lotus per step 3 above.
Investigation on the 3rd occurrence (2026-07-22) ruled out memory (plenty free, no OOM), kernel/hardware events (none logged), and the cache drive (btrfs error counters all zero) — so the "cache drive is the suspect" theory isn't supported by that occurrence's evidence. A brief UPS on-battery power blip landed ~2 min before the crash window, but a second identical blip 90 min later caused no crash, so it's not a reliable trigger on its own. Still looks like an shfs software bug in Unraid 7.3.x tripped by something in the nightly-backup window.
Notes¶
- SSD cache pool added April 2026 — Docker appdata now runs off cache rather than spinning disks
- Static HTML docs site would be read-only and fine on spinning disks