Self-healing monitor stack
BOA runs no external monitoring agent and no resident daemon. Every box watches and repairs itself from a single root crontab that fires a handful of short-lived bash scripts every minute. Those scripts sample load, restart dead services, kill runaway processes, scan auth logs for abuse, and drain the Aegir task queue — then exit. There is nothing long-running to crash; the watchdog is the cron tick.
This topic covers the service / load / process side of that machinery: what cron
launches, the self-looping fan-out that gives it sub-minute reaction time, the per-service
watchdogs, the load auto-pause ladder, the process guards and auth scanners, the box-class
throttle that keeps the fan-out from pinning idle load on small hosts, and the loadreport
profiler used to measure it.
The security-facing members of the same /var/xdrago/monitor/ family — the nginx log
scorer scan_nginx.sh and the CSF ban pipeline — live in the
Abuse Guard topic, and the per-host firewall those bans drive is in
Security & isolation. This topic points at those rather than repeating
them.
The root crontab
The installed root crontab lives at /var/spool/cron/crontabs/root. Its master copy is
aegir/tools/system/cron/crontabs/root, deployed to /var/xdrago/cron/crontabs/root and
copied into place on every install/upgrade. Do not hand-edit the installed file — its own
header says so, and the next upgrade overwrites it. Per-box additions go in
/var/xdrago/cron/custom.txt, which BOA appends after the copy.
| Cmd | Cadence | Role |
|---|---|---|
second.sh |
every minute (self-loops ×10) | Load sampling + auto-pause + heavy watchdog/scanner fan-out |
minute.sh |
every minute (self-loops) | Service auto-healing watchdog fan-out (nginx, php, mysql, …) |
guest-fire.sh |
every minute | Apply temporary CSF web bans (Abuse Guard) |
runner.sh |
every minute (nice -n5 ionice -c2 -n7) |
Drain the Aegir hosting task queue (/var/xdrago/run-*) |
ip_access.sh |
every 2 min | Maintain per-site IP access rules |
ai_policy.sh |
every 2 min | Apply the AI-crawler policy |
nginx_deny.sh |
every 2 min | Regenerate the nginx ban geo from current CSF state (Abuse Guard) |
migration_proxy_realip.sh |
every 5 min | Refresh migration-proxy realip ranges |
clear.sh |
every 5 min | Periodic cleanup |
loadreport --log |
every 30 min (nice -n10 ionice -c3) |
Profile which monitor scripts cost CPU/RSS (read-only) |
manage_ltd_users.sh |
every 3 min (nice -n5 ionice -c2 -n7) |
Maintain limited shell users |
manage_solr_config.sh |
every 4 min (nice -n5 ionice -c2 -n7) |
Maintain per-site Solr config |
guest-water.sh |
05:01 daily | Escalate repeat offenders to persistent csf.deny (Abuse Guard) |
cloudflare_realip.sh |
04:45 daily | Refresh Cloudflare realip ranges |
purge_binlogs.sh |
hourly (:01) |
Purge MySQL binary logs |
mysql_cleanup.sh |
hourly (:30) |
MySQL housekeeping |
mysql_backup.sh / mysql_cluster_backup.sh |
01:15 / 02:15 daily | SQL dumps |
graceful.sh |
03:01 daily | Graceful service cycle |
backboa backup / duobackboa backup |
03:15 / 05:15 daily | Off-site backups |
owl.sh |
04:15 daily (nice -n5 ionice -c2 -n7) |
Nightly maintenance orchestrator — fans out the night/ workers (per-account, per-site, global post): LE renewals, contrib/module maintenance, ghost cleanup |
The per-minute monitors (second.sh, minute.sh, guest-fire.sh, runner.sh) are the
live self-healing core. The 2-minute and longer jobs are slower-changing maintenance. The
nightly owl.sh run has its own page — Nightly maintenance (owl.sh);
the dump/backup jobs are out of scope here — see Backups.
Where the pieces live
The repo source under aegir/tools/system/ is deployed verbatim to /var/xdrago/ on the
box (cp -af .../aegir/tools/system/* /var/xdrago/). So a file the source tree calls
aegir/tools/system/minute.sh is /var/xdrago/minute.sh at runtime. The single-shot
monitors live one level down in /var/xdrago/monitor/check/. loadreport is the
exception — it is deployed to /opt/local/bin/loadreport with a /usr/local/bin/loadreport
symlink, like fpmreport.
| Launcher (repo → box) | What it drives |
|---|---|
aegir/tools/system/minute.sh → /var/xdrago/minute.sh |
Per-pass fan-out of the per-service watchdogs (monitor/check/{system,unbound,valkey/redis,mysql,php,fpm_tune,nginx,nginx_guard,java}.sh) |
aegir/tools/system/second.sh → /var/xdrago/second.sh |
Load sampling / auto-pause, the _proc_control service guards, the hackcheck/hackftp/escapecheck scanners |
aegir/tools/system/runner.sh → /var/xdrago/runner.sh |
Drains the /var/xdrago/run-* Aegir hosting tasks, gated by load and queue state |
aegir/tools/bin/loadreport → /opt/local/bin/loadreport |
Read-only /proc profiler; JSONL log every 30 min |
Every per-service watchdog launched by minute.sh re-sources /root/.barracuda.cnf on
entry (so every _VAR override below is read fresh each pass) and exits immediately unless
/var/log/boa/reset_no_new_password.pid exists — i.e. the auto-healing watchdogs stay
dormant until the box is a fully installed BOA system. The second.sh process guards and
the launchers themselves do not gate on that marker.
Pages in this topic
- Service auto-healing watchdogs — the
minute.shfan-out and themonitor/check/*single-shot watchdogs that restart dead services (nginx, php-fpm, mysql, unbound, valkey/redis, java, system), the OOM cascade, and the cooldown / double-check / load-gating patterns they share. - Load control & auto-pause —
second.sh's per-CPU load model, the SPIDER / TASK / MAX / CRIT thresholds and their_CPU_*_RATIOdefaults, the escalation ladder, the/run/*_load.pidstate files, and incident reporting. - Process guards & auth scanners — the
_proc_controlservice guard set, thehackcheck/hackftp/escapecheckauth and shell-escape scanners, the flood guards, and therunner.shtask-queue runner. - Cron cadence & idle-load throttle — why an idle box shows
load 3–4, the
_monitor_box_classCI / SLOW / NORMAL classification, the per-class cadence, and the_MONITOR_FANOUT_ITER/_MONITOR_FANOUT_SLEEP/_MONITOR_HEAVY_EVERYoverrides. - loadreport profiler — the read-only
/procprofiler that attributes CPU and RSS to each monitor script, its JSONL history log, and how to read its output to find the costly pass. - Nightly maintenance (owl.sh) — the 04:15 nightly orchestrator and
its
/var/xdrago/night/workers, the frozen run context, per-account logs, and the_NIGHT_PARALLEL/_NIGHT_MAX_PARALLELfan-out. - Ghost & empty-artifact cleanup — the opt-in dry-run-by-default reapers for ghost codebases, empty platforms, orphaned vhosts/sites/aliases, and their always-on safeguards.
Cross-cutting
- The security half of this same
/var/xdrago/monitor/machinery —scan_nginx.sh, the request scorer, and theguest-fire→guest-water→nginx_denyban pipeline — is the Abuse Guard. This topic deliberately does not repeat it. - The firewall those guards drive — temp bans, the allow list, LFD, SYNPROXY — is
Security & isolation. The flood guards in this topic also
pkill/purge runawaycsfprocesses. - Every monitor re-sources its config from
/root/.barracuda.cnfon each spawn; the_CPU_*_RATIOload thresholds, the_INCIDENT_REPORTalert level, and the_MONITOR_*throttle overrides are catalogued in the host control-file reference and the Reference appendix.