Deploy Blood Kings Monitoring
Install the central server on your PHP hosting, then add lightweight agents to the machines and routers you want to watch.
1. Install the server
The server runs the checks, collects what the agents send, sends the alerts and serves the status pages. It is plain PHP with a MySQL database: no image to pull and nothing to build on the hosting.
Measured install time: 1 min (from the release ZIP to the first collector run on a clean PHP 8.2 and MySQL server, following this guide; uploading over FTP and clicking through your hosting panel come on top)
Check what the hosting offers
- PHP 8.2 or newer with the PDO MySQL, cURL, mbstring and OpenSSL extensions
- A MySQL or MariaDB database
- A cron job every minute (cPanel's Cron Jobs is enough)
- Apache with .htaccess and mod_rewrite, as on most shared hosting
- One domain or subdomain whose root holds both /status and /app: the app talks to /status on its own host
- Outbound HTTP and HTTPS from the hosting, for the checks
Put it outside the network you watch. A server at home goes down with the home line, and then nothing is left to tell you.
Download and upload
Latest release: —
Unzip it and upload status/ into public_html/status/ and app/ into public_html/app/. Keep app/.htaccess: without it, reloading a page inside the app answers 404. To check the download against the published checksum, save SHA256SUMS next to the ZIP and run:
sha256sum -c SHA256SUMS --ignore-missingNo release yet? Build the same two folders from the source
With git and Node.js 22 on your computer:
git clone --recursive https://github.com/BKPepe/monitoring.git cd monitoring npm ci npm run build -w apps/monitor cp agents/vps-agent/agent.sh agents/vps-agent/agent.py agents/vps-agent/agent.ps1 \ agents/vps-agent/agent_openwrt.sh agents/vps-agent/docker-compose.agent.yml apps/status/Then upload apps/status/ (without its tests/ folder) as status/ and apps/monitor/dist/ (with its .htaccess) as app/. The agents belong next to agent_api.php: that is where they download from and update themselves from.
Create an empty database
Create a MySQL or MariaDB database and a user with all privileges on it (in cPanel: MySQL Databases), and note the name, the user and the password. Leave it empty: the installer creates the tables. Only if you prefer to do everything by hand, import status/schema.sql yourself:
mysql -u YOUR_DB_USER -p YOUR_DB_NAME < schema.sqlRun the installer
Open https://YOUR-DOMAIN/app/setup. It checks PHP, tests the database login, writes status/config.php (or shows the file to upload when PHP may not write there), creates the tables and then the first administrator. There is no default account or password, and the installer refuses to run again once an account exists. Until then it is open to anyone who finds the address, so create the database before the upload and run the installer right after it. By hand instead: copy config.sample.php to config.php in status/ and fill in the connection:
define('DB_HOST', 'localhost'); define('DB_PORT', 3306); define('DB_NAME', 'YOUR_DB_NAME'); define('DB_USER', 'YOUR_DB_USER'); define('DB_PASS', 'YOUR_DB_PASSWORD');Optional keys go into the same file as constants, in capitals, or later into the app under Settings: CRON_KEY, AGENT_REGISTRATION_TOKEN and METRICS_TOKEN. The installer gives every new install its own CRON_KEY. On the manual path it stays empty until you set one, and while it is empty anyone who knows the address of cron.php can start a collection run.
config.php holds the database password. It is not in the ZIP, and uploading a new release does not overwrite it.
Run the collector every minute
One cron job runs every check. The installer's last step shows this line with your server's real path to PHP and to cron.php. In cPanel open Cron Jobs, choose Once Per Minute (* * * * *) and put the command into the Command field. USER is your hosting account; cPanel shows the home directory on its main page.
php -q /home/USER/public_html/status/cron.php >/dev/null 2>&1Without cPanel, the same job as a line for crontab -e:
* * * * * php -q /home/USER/public_html/status/cron.php >/dev/null 2>&1Log in
Log in at https://YOUR-DOMAIN/app/ with the account the installer created. After the manual path, /app/setup creates that first account instead.
Check that it runs
The installer waits for the first collector run and turns green when it lands, within a minute of adding the cron job. To check it later, or after the manual path (the address also opens in a browser):
curl -s "https://YOUR-DOMAIN/status/api.php?action=collection_health"Look for a lastRunAt time and "stale": false. "stale": true with lastRunAt null means the collector has never run: check the cron line and its path.
Next: add monitors in the app, and agents on the machines and routers you want to see inside. Install an agent →
2. Agents on your machines and routers
To see what happens inside a machine (CPU, memory, disks, network) or on a router (Wi-Fi, WAN, connected clients), install an agent on it.
Easiest from your own app: Infrastructure → Add New Monitor → OpenWrt Router or VPS Agent, then save. The dialog prints these steps with your address and the monitor’s key filled in. Below, https://YOUR-DOMAIN and YOUR_AGENT_KEY stand in for them.
A Bash agent. It reads the server address and its key from agent.cfg next to the script and reports over HTTPS; cron runs it every 5 minutes.
- Latest version
- —
- Needs
- Bash, cron and wget; curl for registration with a token. Root for /opt.
- Read the script first
- agent.sh
Install
Download the agent
mkdir -p /opt/bk-agent && wget -O /opt/bk-agent/agent.sh https://YOUR-DOMAIN/status/agent.sh && chmod +x /opt/bk-agent/agent.shCreate the config with the server address and key
cat > /opt/bk-agent/agent.cfg <<'EOF' API_URL="https://YOUR-DOMAIN/status/agent_api.php" AGENT_KEY="YOUR_AGENT_KEY" AUTO_UPDATE="1" EOFThe file must sit in the same folder as the script.
Run the agent by hand and check its output
/opt/bk-agent/agent.sh --verboseAdd the agent to cron, every five minutes
( crontab -l 2>/dev/null || true; echo '*/5 * * * * /opt/bk-agent/agent.sh >/dev/null 2>&1' ) | crontab -
( crontab -l 2>/dev/null | grep -v /opt/bk-agent/agent.sh || true ) | crontab -
rm -rf /opt/bk-agentThen delete the agent’s monitor in the dashboard so its key stops working.
A POSIX sh script for OpenWrt and Turris OS, no bash or Python. It reads the server address and its key from agent_openwrt.cfg next to the script; cron starts it every minute and it exits after each run.
- Latest version
- —
- Needs
- OpenWrt or Turris OS with cron and ubus; curl, uclient-fetch or wget. Optional: iw (channel load) and smartmontools (SMART).
- Read the script first
- agent_openwrt.sh
Install
Download the agent
wget -O /usr/bin/agent_openwrt.sh https://YOUR-DOMAIN/status/agent_openwrt.sh && chmod +x /usr/bin/agent_openwrt.shIf wget cannot do HTTPS, use uclient-fetch with the same arguments.
Create the config with the server address and key
cat > /usr/bin/agent_openwrt.cfg <<'EOF' API_URL="https://YOUR-DOMAIN/status/agent_api.php" AGENT_KEY="YOUR_AGENT_KEY" AUTO_UPDATE="1" EOFThe file must sit in the same folder as the script.
Run the agent by hand and check its output
/usr/bin/agent_openwrt.sh --verboseThe first run does not know the CPU load yet; the agent measures it on the next run.
Add the agent to cron, once a minute
mkdir -p "${HOME:-/root}/.cache/crontab" && ( crontab -l 2>/dev/null | grep -v agent_openwrt.sh || true; echo '* * * * * /usr/bin/agent_openwrt.sh >/dev/null 2>&1' ) | crontab -Check that the agent is scheduled and reporting
crontab -l | grep agent_openwrt.sh && sleep 70 && logread | grep -i agent | tail -5The first line must print the schedule entry. Within a minute the monitor in the app stops reporting a silent agent.
Optional: Wi-Fi 6E support of the clients
opkg update && opkg install hostapd-utilsWithout this package the agent cannot tell which clients support 6 GHz.
( crontab -l 2>/dev/null | grep -v agent_openwrt.sh || true ) | crontab -
rm -f /usr/bin/agent_openwrt.sh /usr/bin/agent_openwrt.sh.* /usr/bin/agent_openwrt.cfg /usr/bin/agent_openwrt.loglines-off
rm -rf /var/run/status-agent-openwrt /tmp/status-agent-openwrt*Then delete the agent’s monitor in the dashboard so its key stops working.
A PowerShell agent. It has no register command: put the agent key from the dashboard into agent.cfg, and the Task Scheduler runs it every 5 minutes.
- Latest version
- —
- Needs
- Windows PowerShell 5.1 or newer, run as administrator.
- Read the script first
- agent.ps1
Install
Download the agent
New-Item -ItemType Directory -Force C:\bloodkings | Out-Null; Invoke-WebRequest -Uri "https://YOUR-DOMAIN/status/agent.ps1" -OutFile C:\bloodkings\agent.ps1Create the config with the server address and key
Set-Content -Path C:\bloodkings\agent.cfg -Value @('API_URL="https://YOUR-DOMAIN/status/agent_api.php"', 'AGENT_KEY="YOUR_AGENT_KEY"', 'AUTO_UPDATE="1"')The file must sit in the same folder as the script.
Run the agent by hand and check its output
powershell.exe -ExecutionPolicy Bypass -File C:\bloodkings\agent.ps1 -VerboseSchedule a run every five minutes (PowerShell as administrator)
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument '-ExecutionPolicy Bypass -File "C:\bloodkings\agent.ps1"' $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 5) -RepetitionDuration (New-TimeSpan -Days 3650) Register-ScheduledTask -TaskName "BloodKingsAgent" -Action $action -Trigger $trigger -User "SYSTEM" -RunLevel Highest
Unregister-ScheduledTask -TaskName "BloodKingsAgent" -Confirm:$false
Remove-Item -Recurse -Force C:\bloodkingsThen delete the agent’s monitor in the dashboard so its key stops working.
The Python agent in a python:3-alpine container. Fill in the agent key from the dashboard.
- Latest version
- —
- Needs
- Docker with the compose plugin. The container uses pid: host and network_mode: host and mounts / read-only, so it reports the host, not itself.
- Read the script first
- agent.pydocker-compose.agent.yml
Install
Download the agent
mkdir -p /opt/bk-agent && cd /opt/bk-agent && wget -O agent.py https://YOUR-DOMAIN/status/agent.py && wget -O docker-compose.agent.yml https://YOUR-DOMAIN/status/docker-compose.agent.ymlFill the server address and key into docker-compose.agent.yml
sed -i 's|STATUS_API_URL: .*|STATUS_API_URL: "https://YOUR-DOMAIN/status/agent_api.php"|; s|STATUS_AGENT_KEY: .*|STATUS_AGENT_KEY: "YOUR_AGENT_KEY"|' docker-compose.agent.ymlStart the container
docker compose -f docker-compose.agent.yml up -dCheck the container output
docker compose -f docker-compose.agent.yml logs -f
cd /opt/bk-agent && docker compose -f docker-compose.agent.yml down
rm -rf /opt/bk-agentThen delete the agent’s monitor in the dashboard so its key stops working.
The same Bash agent as on Linux.
- Latest version
- —
- Needs
- Raspberry Pi OS (ARMv7 or ARM64) with cron and wget; curl for registration with a token.
- Read the script first
- agent.sh
Install
Download the agent
mkdir -p /opt/bk-agent && wget -O /opt/bk-agent/agent.sh https://YOUR-DOMAIN/status/agent.sh && chmod +x /opt/bk-agent/agent.shCreate the config with the server address and key
cat > /opt/bk-agent/agent.cfg <<'EOF' API_URL="https://YOUR-DOMAIN/status/agent_api.php" AGENT_KEY="YOUR_AGENT_KEY" AUTO_UPDATE="1" EOFThe file must sit in the same folder as the script.
Run the agent by hand and check its output
/opt/bk-agent/agent.sh --verboseAdd the agent to cron, every five minutes
( crontab -l 2>/dev/null || true; echo '*/5 * * * * /opt/bk-agent/agent.sh >/dev/null 2>&1' ) | crontab -
( crontab -l 2>/dev/null | grep -v /opt/bk-agent/agent.sh || true ) | crontab -
rm -rf /opt/bk-agentThen delete the agent’s monitor in the dashboard so its key stops working.
Many machines: register with a token
The Bash and OpenWrt agents can create their own monitor. Download the script (the first step on its tab), then run the line below instead of writing the config: the server answers with a new key, and the agent stores it in agent.cfg (agent_openwrt.cfg on a router) next to the script. Then continue with the test run and cron. Both arguments are positional; without the address the agent registers against its localhost default.
/opt/bk-agent/agent.sh --register YOUR_REGISTRATION_TOKEN https://YOUR-DOMAIN/status/agent_api.php/usr/bin/agent_openwrt.sh --register YOUR_REGISTRATION_TOKEN https://YOUR-DOMAIN/status/agent_api.phpThe token is in your app under Settings → Agent Auto-Registration Token. While it is empty, the server accepts the cron key (CRON_KEY) in its place, so whoever has the cron key can add monitors. The Python, PowerShell and Docker agents have no register command.
The agent does not show up in the app
Three checks per platform, in this order: is the agent scheduled, what does one run by hand print, and what is at the end of its log.
crontab -l | grep agent.sh
/opt/bk-agent/agent.sh --verbose
tail -n 50 /opt/bk-agent/agent.logcrontab -l | grep agent_openwrt.sh
/usr/bin/agent_openwrt.sh --verbose
tail -n 50 /tmp/status-agent-openwrt.log; logread | grep -i agent | tail -n 20Get-ScheduledTask -TaskName "BloodKingsAgent"
powershell.exe -ExecutionPolicy Bypass -File C:\bloodkings\agent.ps1 -Verbose
Get-Content C:\bloodkings\agent.log -Tail 50cd /opt/bk-agent && docker compose -f docker-compose.agent.yml logs --tail 50The server marks an agent monitor down after 50 minutes without a report. The limit is in Settings → Timeout to Mark Agent Offline (minutes); 0 turns the check off.
Want to contribute?
The Blood Kings Monitoring system is open-source under the MIT License. The bundled PHPMailer (e-mail sending) keeps its own LGPL-2.1 licence, see THIRD_PARTY_NOTICES.md. Contributions, documentation guides, and issue reports are welcome on GitHub.