# ━━━━━ RSYNC ━━━━━ The transfer engine for the two-server ecosystem. `rsync.sh` is the single script called by every orchestrator that moves data between servers — it handles profiles, pre-flight checks, container stops, the actual transfer, and bandwidth logging. Orchestrators decide what to sync and when. `rsync.sh` decides how to do it safely. > **Never schedule rsync.sh directly for daily/weekly syncs.** Use the orchestrators > in `Orchestrators/`. rsync.sh is called directly only for manual runs and the > 30-minute Emby dirty sync, which needs its own cron entry. --- ## ━━━ THE PROBLEM THAT BUILT THIS ━━━ **rsync Alone Isn't Safe Enough for Live Databases** Running rsync against a share while SQLite databases are being written produces corrupt snapshots on the remote. The arr databases, Emby library database, and Authelia session store all write continuously. A plain rsync copies them mid-write. The remote gets a file that opens cleanly but has internal inconsistencies. Fix: profiles stop specific containers before syncing and restart them after. The database is quiesced, rsync runs against a static snapshot, containers come back up. **Each Share Needs Different Behavior** Media shares (Movies, TV) just spread files — nothing stops, no `--delete` (arr cleanup scripts own deletions, and arr_sync ensures both arrs already track incoming files before they arrive). Arr databases need containers stopped, clean SQLite snapshot, restart. Emby has two modes: weekly full-stop clean mirror and 30-minute dirty sync while Emby stays running (WAL files excluded). Critical-Data stops the auth stack, waits for Authelia to come back after restart delay. Fix: the profile system — one script, behavior defined entirely by the profile key. **A Failed Remote Shouldn't Corrupt a Live Sync** If the remote's rootfs is nearly full, an rsync that starts will write partial files then fail mid-transfer, leaving the remote in a worse state than before. If the remote's backing disks are offline, rsync writes to an empty mount point and "succeeds." Fix: pre-flight checks abort before touching anything if remote conditions are wrong. --- ## ━━━ WHAT THIS FOLDER DOES ━━━ One script. One job: move data from this server to the remote safely. `rsync.sh` handles the full transfer lifecycle: 1. Infer or accept a profile for the given directory 2. Run pre-flight checks (connectivity, rootfs, disk temps, remote share exists) 3. Stop containers specified by the profile (both sides) 4. Run rsync with profile flags, excludes, and bandwidth limit 5. Restart containers (with delay if configured) 6. Restart remote containers if dirty-sync profile specifies it 7. Log the transfer to bandwidth_monitor.sh Everything else — deciding which shares to sync, in what order, on what schedule — lives in the orchestrators. --- ## ━━━ RELATIONSHIP TO OTHER FOLDERS ━━━ ``` Media/ arr_sync.sh ── runs before rsync in daily window ──────────► all arrs agree on library ↓ Orchestrators/ ← decides what to sync, when, and in what order daily_sync_maintenance.sh ──────────────────────────────────► rsync.sh (per share) weekly_sync_maintenance.sh ──────────────────────────────────► rsync.sh (emby, critical-data) critical_sync_maintenance.sh ────────────────────────────────► rsync.sh (partnership shares) Note: Emby watch/resume state is NOT synced by rsync — Media/play_state_sync.sh does it over the Emby API every 30 min (CRITICAL_MAINTENANCE_SCRIPTS). Monitors/ bandwidth_monitor.sh ◄─── called by rsync.sh after each sync (--log-transfer) Fallback/ fallback.sh ──── rsync writeback during handback ──► rsync.sh ``` rsync.sh never calls other scripts except `bandwidth_monitor.sh` at the end of a sync. All orchestration logic lives in the callers. arr_sync.sh (Media/) is a peer that runs before rsync in the daily window — it is not called by rsync.sh directly. --- ## ━━━ THE PROFILE SYSTEM ━━━ Profile key = directory basename lowercased. `--profile=name` overrides. | Profile | What It Syncs | Containers Stopped | Notes | |---------|--------------|-------------------|-------| | *(none)* | Media shares (Movies, TV, Music…) | None | Bidirectional spread — no `--delete` in DEFAULT_RSYNC_OPTS. Arr cleanup scripts own deletions. | | `arrs_stack` | Arr databases | Sonarr, Radarr, Lidarr, Prowlarr, Bazarr, Pinchflat | Clean SQLite snapshot | | `critical-data` | Auth stack | Mariadb-Authelia, Redis-Authelia, NPM, Lldap | Authelia has restart delay | | `important-data` | NextCloud + Postgres | Postgres-NextCloud | NextCloud has restart delay | | `emby` | Full Emby mirror | Emby (both sides) | Weekly — Sunday 2:30am | For full profile definitions see `Manual-Rsync.md`. --- ## ━━━ SCRIPTS IN THIS FOLDER ━━━ | Script | Role | When It Runs | |--------|------|-------------| | `rsync.sh` | Core transfer engine — profile resolution, pre-flights, container management, transfer, bandwidth logging | Called by orchestrators; directly for manual syncs | | `media_seed.sh` | First-fill of a new partner — loops every `DAILY_SYNC_SHARES` entry through `rsync.sh --seed` | Phase 3 — dispatched by `partnership_onboard.sh --phase3-only`, triggered from the Partnership tab | `media_seed.sh` is the one script here that is expected to run for weeks. A first seed of HOST1's library is ~28 TB against a 12.5 MB/s `--bwlimit`, which is why it is a phase of its own rather than a step inside the onboard, and why the Partnership tab gives it a Stop button — `rsync.sh` runs `--inplace --partial`, so stopping costs the file in flight, not the share. It answers to `MEDIA_SEED_ENABLED` in `master.conf` — a Tier 2 toggle beside the per-orchestrator ones, still under Tier 1 `RSYNC_ENABLED`. **Off means there is no Phase 3 at all** and onboarding is two phases: the toggle removes the phase rather than disabling a button. Use it for a partner being filled from a moved disk or one that already holds the library. An unset `MEDIA_SEED_ENABLED` reads as on, so a conf that predates the toggle keeps its behaviour. Phase 3 also sets the gate posture it needs and leaves it there: Tier 1 open so `rsync.sh` will move anything at all, every Tier 2 gate closed so the scheduled orchestrators are not competing for the same link and the same disks for the weeks the seed takes. --- ## ━━━ HOW THE SCRIPTS RELATE ━━━ ``` Callers (Orchestrators/) ────────────────────────────────────────────────────── daily_sync_maintenance.sh │ weekly_sync_maintenance.sh rsync.sh /path/to/share [--profile=name] │ critical_sync_maintenance.sh ─────────────────────────────────────────────► │ fallback.sh (writeback) │ ▼ ┌─────────────────────────────────────┐ │ 1. Infer/accept profile │ │ 2. Pre-flight checks │ │ - connectivity │ │ - rootfs / disk temp / disks │ │ - remote share exists │ │ 3. Stop containers (profile) │ │ 4. rsync transfer │ │ 5. Restart containers │ │ 6. Remote restart (dirty sync) │ │ 7. Log to bandwidth_monitor.sh │ └─────────────────────────────────────┘ ```