A status page that keeps beating when your server does not —
one Cloudflare Worker, or one container on a machine you own.
Astro renders the page, a probe checks every monitor once a minute, and
the history lives in a database next to it. On Cloudflare that is one
Worker with a cron trigger and D1, inside the free tier. On your own
hardware it is one container with an interval and a SQLite file — same
source, same schema. nabız is Turkish for "pulse"; the ASCII spelling
nabiz is used throughout.
| Cloudflare | deploying the Worker, custom hostname, limits |
| Self-hosting | Docker, compose, from a checkout |
| Kubernetes | manifests, one replica, seeding in-cluster |
| Monitors | the rows that say what is watched |
| Configuration | every variable, on both runtimes |
| API | JSON, RSS, badge, notices, machine access |
| Upgrading | schema additions, moving between runtimes |
| Installing with an agent | a runbook: commands, checks, and what each error means |
- One probe per monitor per minute: method, expected status, timeout, optional body match.
- Anti-flap: a watched monitor is called down after
fail_thresholdconsecutive failures (default 2); recovery is immediate, and a monitor that is already down when first seen is believed at once. - 90-day uptime bars, and a latency sparkline once there are probes in two different hours — it averages by the hour, so a fresh deployment has no waveform until the clock crosses one.
- Grouped monitors: one row saying how the group is, never how many it speaks for and never their names.
- Operator notices in markdown, with severity and per-language targeting.
- Alerts to Telegram and a webhook on state changes.
- Six languages (en, tr, de, es, fr, zh-CN), light and dark, auto-refresh.
- JSON API, RSS feed, SVG badge,
llms.txt.
Not included: incident timelines, subscriber emails, multi-region probes.
On your own machine
docker run -d --name nabiz -p 8080:8080 -v nabiz:/data \
-e NABIZ_TITLE="status" -e ADMIN_TOKEN="$(openssl rand -hex 32)" \
ghcr.io/productdevbook/nabiz:latestOn Cloudflare
git clone https://github.com/productdevbook/nabiz && cd nabiz && bun install
bunx wrangler d1 create nabiz # id goes into wrangler.toml
bunx wrangler d1 execute nabiz --remote --file schema.sql
bun run deployEither way the page comes up empty and working — a heading with nothing
under it is the correct first sight. Monitors are rows you add next, and
the first bar appears within one probe interval: see
Monitors. Keep the token you generated above; it is what lets you write notices.
Nothing on the page prints it again — docker inspect nabiz will, which
is also a reason not to leave it on a machine other people can reach.
bun install
bunx wrangler d1 execute nabiz --local --file schema.sql # once
bun run dev # :4321, local D1
bun run check # what CI runsEverything under src/lib/ is plain TypeScript against a narrow database
interface (src/lib/db.ts) that D1 already satisfies and SQLite is made to
(src/lib/sqlite.ts). That seam is the whole difference between the two
runtimes: the page, the probes and the state machine are one copy.
MIT. Icons from Lucide (ISC).