Mirror the Public folder from NAS08 to NAS16, copying only files that have changed. The sync must be lightweight enough for Raspberry Pi hardware, non-destructive (no deletes on NAS16), and scheduled via the OMV Task Scheduler. After every run, an HTML email report is sent summarizing what transferred, what folders were affected, and any errors.
NAS16 pulls from NAS08 (rather than NAS08 pushing) so the heavier rsync process runs on the machine with more RAM (16GB vs 8GB), leaving NAS08 as a passive file server during the sync.
/mnt/kingdezigns08/Public/
NFS share mounted on NAS16 via fstab at boot. Mount verified as non-empty before rsync runs.
/export/kingdezignsnas-16/Public/
Local path on NAS16. Script verifies directory exists before writing.
16 - Documents/ Savings Card Forms.pdf kingdezigns08.nas
All three copied successfully on first sync. kingdezigns08.nas included intentionally.
/mnt/kingdezigns08/Public/.Trash-1000/
System trash folder generated by NAS08's desktop environment. Never needed in backup. Excluded via --exclude='.Trash-1000/' in rsync flags.
No longer appears in logs or emails.
NAS16 pulls from NAS08 via the mounted NFS share. rsync runs entirely on NAS16 to keep NAS08 load minimal — NAS08 is passive during the sync window.
NFS share used instead of SSH. Drive already mounted via fstab on NAS16 — no SSH keys or remote execution needed. NFS maps users by UID. Both machines have rufusking as UID 1000 so permissions resolve correctly over the mount.
Files on NAS16 that do not exist on NAS08 are left untouched. NAS16 is a backup, not a strict mirror. This protects NAS16-only files from accidental deletion.
rsync skips files where the NAS16 copy is newer than NAS08. This is lightweight (uses timestamps, not file hashing) and protects any edits made directly on NAS16.
Schedule: Every other day at 2:30 AM via OMV Task Scheduler. NAS08 runs its own nas16-backup.sh at 2:00 AM (finishes ~2:05 AM). The 25-minute buffer prevents overlap on heavy nights.
sudo mkdir /usr/scripts/nas sudo chown rufusking:users /usr/scripts/nas
/usr/scripts already existed. /nas subfolder created manually on NAS16. chown required after sudo mkdir — ownership defaults to root otherwise.
Directory created and ownership set successfully.
scp /path/to/nas08_to_nas16_sync.sh rufusking@192.168.150.40:/usr/scripts/nas/
File transferred successfully.
sudo chmod +x /usr/scripts/nas/nas08_to_nas16_sync.sh
Run directly on NAS16 (not via remote SSH). Makes the script executable by the OMV scheduler.
Permission set. Script ready to run.
# Changed in OMV UI → Services → NFS → Shares (all four shares) # Then reloaded on NAS08: sudo exportfs -ra # Then remounted on NAS16 to pick up the new export options: sudo umount /mnt/kingdezigns08 sudo mount /mnt/kingdezigns08
subtree_check was causing intermittent Permission Denied errors on 02 - Photos, 08 - Taxes, and 16 - Documents during scheduled overnight runs. no_subtree_check is the modern recommended default for private NFS setups.
All three folders now accessible. Permission errors resolved.
sudo chown -R rufusking:users /var/log/nas-sync/
Log directory was created by root during the first automated OMV run. Script now self-corrects ownership at startup so this never needs to be done manually again.
Log writing works cleanly. Self-healing ownership check added to script.
Command : /usr/scripts/nas/nas08_to_nas16_sync.sh Schedule: 30 2 */2 * * (2:30 AM every other day)
Scheduled and confirmed running.
/usr/scripts/nas/nas08_to_nas16_sync.sh
/var/log/nas-sync/nas08_to_nas16_YYYY-MM-DD.log
Logs older than 30 days deleted automatically
--archive Preserve permissions, timestamps, symlinks, owner/group --update Skip files where NAS16 copy is newer (protects NAS16 edits) --human-readable Sizes in KB/MB/GB --itemize-changes One structured line per transferred file — used for folder activity parsing --stats Summary totals at end of each run --exclude Excludes .Trash-1000/ from all operations
NFS mount check Waits up to 30s for NFS share to be fully responsive before starting Source empty check Aborts if NAS08 share appears empty — prevents accidental wipe of destination Dest check Confirms NAS16 folder exists before writing Smart lock file Stores PID in lock file; auto-clears stale locks if process is no longer running Log ownership fix Self-corrects /var/log/nas-sync/ ownership on every startup Exit code Returns rsync exit code so OMV scheduler can detect failures
Header/banner SUCCESS / WARNING / FAILED status with color coding
Stats tiles Files scanned, transferred, new files, data moved, duration
Source/dest info IPs, paths, transfer rate, total source size
Folder Activity Collapsed by default — folders with changes show file count,
folders with errors show the error message, no-activity runs
show "all files up to date"
Full rsync log Collapsed by default — complete raw output available on demand
rufus.king@kingdezigns.com
rsync: [sender] opendir "/mnt/kingdezigns08/Public/02 - Photos" failed: Permission denied (13)
Investigation: ACLs confirmed correct on NAS08 (rufusking:rwx present on all affected folders and subfolders). UIDs matched on both machines (UID 1000). Folders readable manually via NAS16 terminal. Error only appeared during scheduled overnight runs.
Root cause: subtree_check in the NFS export config on NAS08 was causing intermittent access failures for folders with ACL-based permissions and mixed ownership (some owned by www-data via Nextcloud). subtree_check forces NFS to validate every file access against the export tree — this check fails intermittently on first access after an idle period, especially overnight.
Fix: Changed all four NFS shares on NAS08 from subtree_check to no_subtree_check via OMV UI. Reloaded exports with sudo exportfs -ra. Remounted share on NAS16. All three folders accessible on next run.
Resolved — no Permission Denied errors since fix applied.
tee: /var/log/nas-sync/nas08_to_nas16_2026-05-17.log: Permission denied
Root cause: /var/log/nas-sync/ was created by root during the first automated OMV run. Running the script manually as rufusking couldn't write to a root-owned directory.
Fix: sudo chown -R rufusking:users /var/log/nas-sync/. Script updated to self-correct ownership on every startup — checks if the log directory owner matches the running user and auto-chowns if not.
Resolved — script is now self-healing for this condition.
ERROR: Lock file exists at /tmp/nas08_to_nas16.lock. Another sync may still be running. Exiting.
Root cause: Script was killed mid-run during testing, leaving the lock file behind. Original lock logic just checked for file existence with no way to distinguish a live run from a stale one.
Fix: Lock file now stores the script's PID. On startup, if a lock file exists the script checks whether that PID is still running. If not, it removes the stale lock and continues with a warning in the log.
Resolved — stale locks are now self-clearing.
Root cause: Original folder parser relied on xfr# markers from rsync's --progress flag. When --progress was removed (to clean up captured output), those markers disappeared and the parser never incremented folder counts. Switching to --verbose didn't help because verbose output only shows directory names, not individual files.
Fix: Replaced --verbose with --itemize-changes, which outputs a structured prefix line for every transferred file regardless of size (e.g. >f+++++++++ 02 - Photos/2020/12/photo.jpg). Parser updated to detect lines starting with >f and extract the top-level folder name.
Resolved — folder activity now correctly shows file counts per folder.
All four shares updated to no_subtree_check on May 18, 2026. The /export root entries are OMV auto-generated read-only entries — leave them as-is.
| Share | Networks | Key Options |
|---|---|---|
/export/kingdezigns-all |
.100 .110 .120 .150 | rw, no_subtree_check, insecure, crossmnt |
/export/kingdezigns08-secure |
.100 .110 .120 .150 | rw, no_subtree_check, insecure, crossmnt |
/export/kingdezigns-public |
.100 .110 .120 .150 | rw, no_subtree_check, insecure, crossmnt |
/export/HomeAssistantBackups |
.150 only | rw, no_subtree_check, insecure, crossmnt |
/export (root) |
.100 .110 .120 .150 | ro, root_squash, subtree_check — OMV auto-generated, do not edit |
Note: duplicate 192.168.100.0/24 entry for /export root exists in OMV config — pre-existing, harmless, clean up in OMV UI when convenient.
| Time | Device | Script | Notes |
|---|---|---|---|
| 12:00 AM | NAS08 | /usr/scripts/zfs/nas08_zfs_report.sh |
Daily ZFS health report email. |
| 2:00 AM | NAS08 & NAS16 | /usr/scripts/omv/nas16-backup.sh |
Pre-existing backup script. NAS16 finishes ~1 min, NAS08 ~5 min. Runs every 3 days. |
| 2:30 AM | NAS16 | /usr/scripts/nas/nas08_to_nas16_sync.sh |
NAS08 → NAS16 Public folder sync. 25-min buffer after NAS08 backup finishes. |
| 3:00 AM (Sun) | NAS08 | /usr/scripts/zfs/nas08_zfs_scrub.sh |
Weekly ZFS scrub. Sundays only. |
| TBD | TBD | Future maintenance tasks | Drive health checks, log cleanups, etc. — to be added later. |
| Lesson | Detail |
|---|---|
| subtree_check causes intermittent NFS permission failures | On folders with ACL-based or mixed ownership, subtree_check can fail the export tree validation intermittently — especially after idle periods overnight. no_subtree_check is the safe modern default for private home networks. |
| NFS client cache must be cleared after export changes | Running exportfs -ra on the server isn't enough. The NFS client (NAS16) must unmount and remount to pick up the new export options. Without this, the old subtree_check behavior persists. |
| ACL permissions and NFS UIDs are separate concerns | Files can have correct ACLs (rufusking:rwx) but still be blocked over NFS if the UID mapping fails. Always verify UIDs match on both machines with id username before assuming ACL issues. |
| sudo operations reset directory ownership | Any sudo mkdir or sudo mount can leave directories owned by root. Scripts that write to those directories will fail with Permission Denied when run as a regular user. Always chown after sudo directory operations. |
| Lock files should store PIDs, not just exist | A bare touch lock file can't distinguish a live run from a stale one left by a killed process. Storing the PID and checking if it's still running makes lock files self-healing. |
| --itemize-changes is the right flag for parsing file transfers | --progress generates xfr# markers but is noisy. --verbose only lists directories. --itemize-changes gives one clean structured line per transferred file (>f prefix) that's easy to parse reliably regardless of file size. |
| Pull is safer than push for backups | Running rsync on the destination keeps the source device (NAS08) passive and low-load during the sync window. |
| Schedule buffer matters | Running the sync immediately after another backup script risks overlap on heavy nights. A 25-30 min buffer between jobs keeps things clean. |
| File count is larger than expected when blocked folders open up | Initial estimate was ~16,000 files. Actual count after Photos/Taxes/Documents became accessible was ~225,000 files across ~983 GB. rsync handled this without issue. |
| Item | Detail |
|---|---|
| New script on NAS16 | /usr/scripts/nas/nas08_to_nas16_sync.sh — document in NAS16 script inventory |
| New log location on NAS16 | /var/log/nas-sync/ — add to log monitoring notes. 30-day auto-rotation. |
| OMV Scheduler — NAS16 | Task added: 30 2 */2 * * running the sync script |
| NFS exports changed on NAS08 | All four shares updated to no_subtree_check in OMV. Document in NAS08 config notes. |
| NAS08 → NAS16 data flow | NAS08 Public folder now syncing to NAS16 Public every other day at 2:30 AM. 225,000 files / 983 GB in scope. |
| OMV duplicate export entry (cleanup pending) | 192.168.100.0/24 appears twice for the /export root in NAS08's exports. Harmless but should be cleaned up in OMV UI when convenient. |