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)

  1. 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.

  2. 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-missing

    No 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.

  3. 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.sql
  4. Run 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.

  5. 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>&1

    Without cPanel, the same job as a line for crontab -e:

    * * * * * php -q /home/USER/public_html/status/cron.php >/dev/null 2>&1
  6. Log 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.

  7. 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

  1. 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.sh
  2. Create 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"
    EOF

    The file must sit in the same folder as the script.

  3. Run the agent by hand and check its output

    /opt/bk-agent/agent.sh --verbose
  4. Add 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 -
Uninstall
( crontab -l 2>/dev/null | grep -v /opt/bk-agent/agent.sh || true ) | crontab -
rm -rf /opt/bk-agent

Then 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.

Bash (Linux, Raspberry Pi)
/opt/bk-agent/agent.sh --register YOUR_REGISTRATION_TOKEN https://YOUR-DOMAIN/status/agent_api.php
OpenWrt / Turris
/usr/bin/agent_openwrt.sh --register YOUR_REGISTRATION_TOKEN https://YOUR-DOMAIN/status/agent_api.php

The 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.

Linux / Raspberry Pi
crontab -l | grep agent.sh
/opt/bk-agent/agent.sh --verbose
tail -n 50 /opt/bk-agent/agent.log
OpenWrt / Turris
crontab -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 20
Windows (PowerShell as administrator)
Get-ScheduledTask -TaskName "BloodKingsAgent"
powershell.exe -ExecutionPolicy Bypass -File C:\bloodkings\agent.ps1 -Verbose
Get-Content C:\bloodkings\agent.log -Tail 50
Docker (the container output)
cd /opt/bk-agent && docker compose -f docker-compose.agent.yml logs --tail 50

The 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.