The Infra Sentinel landing page with the headline Know when it breaks, before your users do, and a preview of uptime numbers

The problem

When a service stops working, someone has to notice, decide who should act, and tell the customers. Infra Sentinel does those three jobs from one place that you host yourself.

It is meant for people and small teams who host their own services and want monitoring, incidents and status pages without a paid service, and for developers who want to read or reuse the code.

The approach

A web console, a REST API, a Go agent and a Go command line tool share one PostgreSQL database. The Next.js web app serves the console and forwards /api/v1 and /status to the NestJS API, which runs the checks, the alert engine and the workers in the same process.

PostgreSQL also holds the job queues, locks and hourly rollups, so there is one thing to install and back up, and no Redis or message broker. Every dependency is open source, and nothing needs a paid service or an API key.

What it does

  • HTTP(S), keyword, TCP, ping, DNS, SSL certificate and heartbeat monitors
  • Assertions on the status code, on text and on a JSON path, and a state change only after a set number of failures in a row
  • Incidents opened and closed by an alert engine, with timelines, notes and postmortems
  • Alerts by email, Slack, Discord, Telegram, ntfy, Gotify or a signed webhook
  • Escalation policies, on-call schedules, silences and maintenance windows
  • Public status pages rendered as plain HTML, with an RSS feed and email subscribers
  • SLA reports as a page, a PDF or a CSV
  • A Go agent that reports CPU, memory, disks, network, containers and services
  • A live console fed by server-sent events
  • Four workspace roles, scoped API keys, signed webhooks and an audit log with CSV export
  • Two-factor sign-in with an authenticator app, and single sign-on with OpenID Connect
  • English, French and Arabic, with a right-to-left layout
  • A command line tool, sentinel, written in Go

Where the engineering is

Queues without Redis

The check scheduler wakes up every second, claims the monitors that are due and runs up to 20 checks at a time. Notifications are jobs in PostgreSQL too: a worker claims a job, sends it and tries again up to three times if it fails.

Jobs are claimed with SELECT ... FOR UPDATE SKIP LOCKED inside a transaction, so a second API process cannot take the same job, and a crash loses nothing because the claim never committed.

Incidents that are never lost or doubled

Every two seconds the alert engine looks for monitors that are down with no open incident, opens one and queues a notification for each channel. When the monitor recovers, it resolves the incident and sends the recovery message.

Because it compares the state of monitors with the open incidents, instead of reacting to single events, a restart or a second instance does not change the result.

An open incident with its timeline, assignee, details and notifications, Open the full-size image (opens in a new tab)
An open incident with its timeline, who was notified and the notes written so far.

A live console without WebSockets

Server-sent events deliver changes to the console, and TanStack Query invalidates what changed. When the stream drops, the console falls back to refreshing every 30 seconds.

The overview with open incidents, monitor and uptime numbers, and the servers, Open the full-size image (opens in a new tab)
One screen for what needs attention now: open incidents, uptime and response numbers, and the servers.

Secure by default

Passwords are hashed with scrypt. Sessions are stored as hashes and sent in an HttpOnly cookie, so signing out ends the session at once. Values the API must read back, such as two-factor seeds and webhook secrets, are encrypted with AES-256-GCM.

Sign-in and API calls are rate limited, an option stops monitors from probing private addresses, and the web app sets a Content-Security-Policy and other security headers. Migrations are plain SQL, applied at start-up under a PostgreSQL advisory lock, so several instances can start together.

Right-to-left Arabic

The language is chosen on the server from a cookie or the browser setting, so pages arrive already translated, and Arabic mirrors the whole layout.

The overview in Arabic with the layout mirrored, Open the full-size image (opens in a new tab)
Arabic mirrors the whole layout.

One command checks everything

There is no CI service, on purpose. One script, bash scripts/verify.sh, runs every check and prints one table: syntax, documentation links, builds, unit and end-to-end tests against a real PostgreSQL, the Go agent and command line, browser tests with Playwright, a backup and restore check, and the production images.

The result table of the verification script with every check passing, Open the full-size image (opens in a new tab)
The recorded verification run, with every check passing.

Screens

How it's built

LayerTechnology
APINestJS 11 and TypeScript 5.7, zod request schemas, plain SQL with node-postgres, generated Swagger documentation
WebNext.js 16, React 19, Tailwind CSS 4 with light and dark themes, TanStack Query 5
Agent and CLIGo 1.22 with the standard library only, one static binary per platform
DatabasePostgreSQL 17 for data, job queues, locks and hourly rollups, with 7 SQL migrations
TestingJest unit and end-to-end tests, Playwright browser tests and Go tests
OperationsA Docker production setup, and backup and restore scripts with a restore check
Infra Sentinel architecture: the browser, a monitored server and the command line talk to the Next.js web app and the NestJS API, which uses PostgreSQL and sends alerts, Open the full-size image (opens in a new tab)
Two processes and one database: the web app, the API with its background workers, and PostgreSQL.

Results

  • 68,750 lines of code and tests, counted from the repository.
  • 151 API endpoints in 17 controllers, and 21 web pages, 14 of them behind sign-in.
  • 43 database tables in 7 migrations.
  • 7,902 lines of Go with no external modules.
  • 444 API unit tests, 54 end-to-end tests and 25 browser tests passed in the recorded verification run.
  • 3 languages in the console and 16 documentation pages.

What I would build next

  • Monitor groups, and checks from several regions instead of only the machine that hosts the API.
  • Domain expiry checks, flapping detection and p50 response times.
  • Maintenance windows in a chosen time zone, with end dates.
  • On-call layers and overrides, and a message to the assignee when an incident is assigned.
  • SMS, phone, Microsoft Teams and PagerDuty channels; the signed webhook covers custom integrations for now.
  • Stored container history, a fuller macOS agent and a Windows agent.

Built with

  • TypeScript
  • NestJS
  • Next.js
  • React
  • PostgreSQL
  • Go
  • Tailwind CSS
  • Playwright

56 results