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); ?>
Tools tool
$_fs) foreach ($_fs as $_fid) $_inFolder[] = $_fid; ?>
Custom Scripts script
Scheduler Information
/ scheduled ·
How do I use this
  • Toggle — saves immediately to schedule.json
  • Cron field — auto-saved on focus-out; hint shows plain English; red border = invalid
  • ⚙ Cog — opens settings panel: script header + matching README/Manual sections + config fields
  • Script name — click navigates to the matching entry in the Orch tree (right panel)
  • Run — fires script immediately; resets the cron timer so next fire is one full interval later
  • Dry Run — same as Run but passes --dry-run; no changes written to disk
  • Log — opens script log here; auto-scrolls; use Search box to filter/highlight lines
  • Stop — SIGTERM → 3 s → SIGKILL; clears stuck lock files
  • Verbose — appends --log flag for per-item detail output
  • Cancel — discards changes and returns here; file is never touched until Save
  • ⚡ Array events — set any cron field to array_start or array_stop; badge appears; fires on Unraid array start/stop; works on any orch or custom script
  • Arrange — drag scripts between orchs; drop to right panel removes from orch; Save Arrangement commits to master.conf
  • + Folder — collapsible subfolder in Custom Scripts; drag scripts in
  • Suggested cron — click the cron code badge in the Orch tree to apply and save instantly
  • Status dot (tree) — green/orange/red ● = last run ok/warn/error; dim = never run; hover for detail
  • Cron Calculator — type an expression or plain English; shows description + next 5 fires; Apply pushes to last-focused cron field
  • Recent Activity — last 24 runs; click a row to open that log; auto-expands on errors
  • Recent Errors — last error per script (7 days); click to open log; auto-expands on new errors
  • Advanced — top-right button, turns blue; ⚙ shows enriched view (header + docs + config); raw conf editing unlocked
  • Log search — filter box in toolbar when log is open; highlights matches, dims others
  • ← Scheduler Info — returns here from any log, editor, conf, or script view
  • Orchestrator & Child Behaviour
  • Orch ON — sole trigger; cron fires the orch; it calls children in sequence
  • Orch OFF — never runs automatically; children suppressed; each can get its own standalone cron
  • Child (orch ON) — toggle comments/uncomments the script in master.conf
  • Child (orch ON, no array) — orch hardcodes the call; toggle is display-only
  • Child (orch OFF) — enter a cron to run it standalone
  • Rsync badge — toggle writes TIER_RSYNC_ENABLED directly to master.conf
  • Rsync (orch OFF) — fill location + standalone cron + Save; both required for independent firing
  • Editor Keyboard Shortcuts
  • Ctrl+S — save · Ctrl+Z / Ctrl+Y — undo / redo (200 steps)
  • Ctrl+/ — toggle line comment · Tab / Shift+Tab — indent / dedent
  • Ctrl+F — find · Ctrl+H — find & replace · Ctrl+G — go to line
  • Ctrl+D — select next occurrence · Ctrl+L — select line · Ctrl+Shift+K — delete line
  • Alt+↑/↓ — move line · Alt+Shift+↓ — duplicate line below
  • Home — smart (first non-space → col 0)
  • ( { [ " ` — auto-close pair; wraps selection; Backspace deletes both when between them
  • A− / A+ in status bar — font size (9–22px, persists across sessions)
  • Indent guides — faint vertical lines every 2 spaces in the background
Next Runs
calculating…
Cron Calculator
Recent Activity
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
">