isFile()) continue; if (!preg_match('/\.(sh|md)$/i', $_rf->getFilename())) continue; $_rel = ltrim(str_replace($_repoBase, '', $_rf->getPathname()), '/'); $_parts = explode('/', $_rel); if (in_array($_parts[0], $_repoSkip)) continue; $_repoFiles[] = $_rel; } } catch (Exception $_re) {} sort($_repoFiles); // Docs tree: README and Manual with their per-module children $_mainReadme = in_array('README.md', $_repoFiles) ? 'README.md' : null; $_childReadmes = array_values(array_filter($_repoFiles, function($f) { return preg_match('/^README/i', basename($f)) && $f !== 'README.md'; })); sort($_childReadmes); $_mainManual = in_array('Manual.md', $_repoFiles) ? 'Manual.md' : null; $_childManuals = array_values(array_filter($_repoFiles, function($f) { return preg_match('/^Manual/i', basename($f)) && $f !== 'Manual.md'; })); sort($_childManuals); // Job stats for notification board $totalJobs = 0; $scheduledJobs = 0; foreach ($tree as $orch) { $totalJobs++; if (!empty($orch['cron']) && $orch['enabled']) $scheduledJobs++; foreach (($orch['children'] ?? []) as $child) { $totalJobs++; if (!empty($child['cron']) && $child['enabled']) $scheduledJobs++; } } $runningScripts = []; exec('pgrep -af bash 2>/dev/null', $psLines); foreach ($psLines as $line) { if (preg_match('#' . preg_quote(SCRIPTS_DIR, '#') . '/([^\s]+\.sh)#', $line, $rm)) { $runningScripts[] = basename($rm[1], '.sh'); } } $runningScripts = array_unique($runningScripts); ?>
$_fs) foreach ($_fs as $_fid) $_inFolder[] = $_fid; ?>
Custom Scripts script
Scheduler Information
/ scheduled ·
How do I use this
  • Toggle — saves to schedule.json immediately
  • Cron — 5-field expression; hit Save Schedule to apply all at once
  • Run — fires script immediately regardless of schedule
  • Dry Run — same as Run with DRY_RUN=1
  • Log — opens script log in this panel; auto-scrolls to latest
  • Stop — SIGTERM → 3 s → SIGKILL; clears stuck locks
  • Verbose — appends --log for detailed per-step output
  • Orch ON — sole trigger; cron fires it; it calls children in order
  • Orch OFF — never runs; all children suppressed and get individual cron fields
  • Child (orch ON) — toggle comments/uncomments the script's line in master.conf
  • Child (orch ON, no array) — orch hardcodes the call; toggle shows active but is display-only
  • Child (orch OFF) — give it its own cron to run standalone
  • ⚡ Array events — triggered by Unraid array start/stop; no cron field
  • Rsync badge — writes TIER_RSYNC_ENABLED flag to master.conf directly
  • Rsync (orch ON) — Run/Dry Run/Log hidden; orch controls the rsync call
  • Rsync (orch OFF) — enter a location path and standalone cron, then Save; both must be filled for the cron to fire rsync independently
  • Arrange — drag scripts between orchs or reorder within an orch; right panel shows unassigned scripts that can be dragged in; drag to the right panel removes from orch
  • Save Arrangement — commits all moves and reorders to master.conf
  • + Folder — creates a named collapsible subfolder in Custom Scripts; scripts can be dragged into folders
  • Script name — click opens settings (non-Advanced) or full source editor (Advanced)
  • Suggested cron — click the cron code in the Scripts panel to apply & save it to that card instantly
  • Advanced — top-right button; turns blue when on; script-name click shows full editable source; conf file buttons appear in the status bar
  • ← Scheduler Info — back button returns here from any log, editor, or script view
Next Runs
calculating…
Recent Errors
Active Locks
Partner
$v['enabled'] === false); ?>
Disabled on this host
Fallback
Not enterprise HA. No SLAs, no quorum nodes, no guaranteed uptime — don't put your billing system on this.

What it is: mutual automatic failover between two independent unRAID servers. When one goes down the other starts its containers, cuts over DNS, and keeps users online. When it comes back, everything hands back in the correct sequence — DDNS stops, containers stop, rsync writeback runs, containers start on the primary, primary DDNS starts last — so users hit the returning server only after it's actually ready.

Built from scratch. Refined through a year of production testing. The DDNS sequencing and handback order were the hardest parts to get right. Both directions are exercised regularly with fallback_test.sh.
How it works
This started as...
Docs
README
Manual
Orch