A self-hosted uptime monitor for your websites. Node.js and SQLite, one runtime dependency, no build step. Watch your sites from a browser dashboard, a Telegram bot, or both.
Live demo: uptime-tracker-b8pe.onrender.com
Broken sites sort to the top. The page updates live over server-sent events, so you never reload it to find out what changed.
Every monitor keeps its own response-time history, uptime figures, certificate status and incident log.
- Checks each site on its own schedule, 60 seconds by default, and follows redirects.
- Retries a failure before calling a site down, so one dropped packet does not wake you up.
- Catches timeouts, DNS failures, refused connections, unexpected status codes, expired certificates and missing page text.
- Records every check in SQLite and keeps 90 days of it.
- Warns you weeks before a TLS certificate expires.
- Sends alerts to Telegram, a JSON webhook, or both.
npm installnpm startOpen http://localhost:3001. On Windows you can double-click start.bat instead.
Node 23.4 or newer gives you the built-in SQLite driver. On older versions run
npm install better-sqlite3 and the app will use that.
Your first launch seeds six public sites so the dashboard has something in it. Delete them and add
your own, or set UPTIMETRACK_NO_SEED=1 to start with an empty list.
Click Add monitor for a single site. To load a batch, open Add several at once and paste URLs one per line; names come from the domain.
Each monitor carries its own check interval, timeout, retry count, accepted status codes, slow
limit, alert cadence, and an optional keyword that must appear in the response body. A site behind
a login that answers 401 stays green once you set its accepted codes to 200-299, 401.
Sites are checked hourly by default. That keeps load low across a large list, at the cost of
noticing an outage up to an hour late. Drop your revenue-critical sites to 5 or 15 minutes from the
monitor menu in the bot, or with /every checkout 5m, and leave the rest hourly.
The bot gives you the same monitors from your phone. It polls Telegram over outbound HTTPS, so the server needs no open port, no domain and no certificate for the bot to work.
Create a bot with @BotFather (/newbot), paste the token into the dashboard settings, and press
Start bot. The dashboard shows a pairing code. Send /start <code> to your bot and the first
chat to pair becomes the admin.
Everything runs from an inline keyboard: Status, Recheck all, Monitors, Speed, Alerts and Digest now. Open a monitor from the list and you get buttons for check-now, mute, check interval, ping interval, slow limit, speed test, pause and delete. Commands still work as shortcuts.
| Command | Who | What it does |
|---|---|---|
/menu |
any paired chat | Opens the button menu |
/status [name] |
any | Everything, broken first, or one site in detail |
/recheck |
any | Forces a check of every site |
/report |
any | The daily table on demand |
/speed [name] |
any | Page-speed results, or samples one site now |
/mute [1h|6h|24h], /unmute |
any | Silences alerts in that chat |
/every <site> <15m> |
admin | How often that site is checked |
/repeat <site> <2h|off> |
admin | How often that site re-pings while broken |
/slow <site> <3s|off> |
admin | Its slow limit |
/add <url> [name], /remove <name> |
admin | Adds or deletes a monitor |
/pause, /resume |
admin | Suspends checks |
/chats, /approve <id>, /kick <id> |
admin | Controls who receives alerts |
Healthy sites produce no messages. You hear from the bot when something breaks.
Down and recovery alerts reach every paired chat with notifications on, so a team group learns about an outage at once. Each alert carries 🔇 1h / 6h / 24h buttons that mute that one monitor for the chat that tapped them, plus a re-check button.
Slow sites get their own amber alert. When the average of the last few checks crosses the slow
limit (3 seconds by default), the bot flags it and repeats every 2 hours, half as often as the
hourly reminder for a site that is fully down. Set a limit per site with /slow, and a per-site
ping cadence with /repeat.
Several sites failing together arrive as one message rather than a burst. Certificate warnings go out at 30, 14, 7, 3, 1 and 0 days and reset once you renew.
At 10:00 UTC the bot posts a table covering every site: state, 24-hour uptime and average
latency, sorted with problems on top. It arrives silently when everything is healthy and with a
notification when something needs attention. Change the hour in the dashboard settings, or leave it
blank to switch the report off. /report prints it on demand.
SITE STATE 24H AVG
───────────────────────────────────
Checkout DOWN 97.2% —
Marketing SLOW 100% 6.0s
API UP 100% 200ms
PageSpeed Insights only measures Chromium, so it tells you nothing about Safari or any iOS browser. This samples Chromium and WebKit, at a desktop and a mobile viewport, and records load time, first contentful paint, largest contentful paint and transfer size.
A real browser costs far more than an HTTP request, so it runs on a slow cadence: once every 5 days per site by default, one site at a time. When the worst of the four runs crosses 5 seconds, the bot alerts with a breakdown per engine. Press ⚡ Speed test on any monitor to sample it immediately.
This part is optional and off until you install the browsers:
npm install playwrightnpx playwright install chromium webkitThen enable it in the dashboard settings. Without Playwright the rest of the app is unaffected and the Speed screen says so.
The bot answers only chats you have paired. An unknown chat gets a refusal and nothing else, so nobody learns which sites you watch by messaging your bot. Admin and member chats are separate: members read status, admins change it.
NDA mode, on by default, strips URLs out of group chats and shows monitor names on their own. Private chats with an admin still show the full address. If someone blocks or removes the bot, it drops that chat.
| Variable | Default | Purpose |
|---|---|---|
PORT |
3001 |
HTTP port |
HOST |
0.0.0.0 |
Bind address |
UPTIMETRACK_AUTH |
off | user:password for HTTP basic auth. Set this on any deployment other people can reach; the app warns you when it is missing |
UPTIMETRACK_DB |
./data/uptimetrack.db |
Database location |
RETENTION_DAYS |
90 |
How long to keep check history |
TELEGRAM_BOT_TOKEN |
off | Starts the bot at boot without touching the UI |
UPTIMETRACK_NO_SEED |
off | 1 starts with no example monitors |
Saved secrets never travel back to the browser. The settings form shows ******** for a stored
token; leave it to keep the token, clear the field to remove it. GET /api/health skips
authentication so container and uptime probes can reach it.
DEPLOY.md covers a Docker Compose setup, a systemd unit, Caddy and nginx configs, and the Telegram walkthrough. Two rules worth following: run it somewhere other than your laptop, and run it somewhere other than the server hosting the sites it watches. A monitor that dies alongside the thing it monitors tells you nothing.
For confidential sites, skip the reverse proxy. Keep the app bound to 127.0.0.1, close every port
except SSH, use the bot day to day, and tunnel in when you want a chart:
ssh -L 3001:127.0.0.1:3001 you@your-serverlib/monitor.js schedules and runs the checks, then emits events. The web server and the Telegram
bot both subscribe to those events, which keeps them independent of each other.
lib/alerts.js decides what reaches a human: it batches simultaneous failures, throttles repeat
reminders and tracks certificate thresholds. lib/db.js holds the SQLite schema and queries.
The database sits next to the app under data/. Keep that directory out of version control, since
it holds the URLs you monitor and your notification tokens. If you park the project inside a synced
folder such as OneDrive, sync can hold the file open now and then; the app retries for five
seconds, though an unsynced directory suits it better.
MIT