I wanted to know when my sites go down before anyone tells me — without renting that knowledge from a third party. So I built Monitor: a self-hosted uptime monitor that watches websites, services and machines, records what happened, and shows it on a dashboard that updates itself while you look at it.
What it checks
There are nine monitor types: plain website checks, keyword detection, JSON API endpoints, multi-step API sequences, ping, open ports, SSL certificate expiry, domain registration, and DNS records. They all share the same knobs — interval, timeout, retries before an incident opens, and a response-time threshold — so adding a check takes seconds, not documentation.
When something does break, an incident opens, an email goes out via SMTP or sendmail, and the dashboard shows it until someone acknowledges it. Every change is audit-logged.
The agent that listens on nothing
Uptime checks only tell you a machine answers. For the machines I own, a small agent reports the rest: hardware metrics, disk usage, pending updates, and running services. The part I'm most pleased with is that the agent listens on nothing — it only ever calls out to the dashboard, so there is no port to open and no new attack surface on the monitored machine.
Keeping the charts fast
Naively storing every check result makes charts slower every week you run the thing. Monitor aggregates responses into per-minute, per-hour and per-day buckets as they arrive, so the 30-day charts — response times and uptime bars — render just as fast after a year of history as after a day.
Boring on purpose
The stack is plain PHP 8 and MySQL: no Node, no build step, no container. That is not nostalgia, it's an availability argument — a monitoring tool is the one thing that must keep running when everything else is on fire, so it should have as few moving parts as possible.
Boring does not mean careless: passwords are hashed with Argon2id, every query is parameterized, forms carry CSRF tokens, private and loopback targets are refused by default, and the database password lives in a .env file one level above the web root — the same trick this site uses. Monitor types are registered classes with their config stored as JSON, so adding a tenth type means implementing one interface and no database migration.
Try it
Clone it, run composer install, create a database, and a wizard walks you through the rest; one cron line runs the scheduler every minute. It's MIT-licensed, and the code is on GitHub — issues and pull requests welcome.