One Go binary, one YAML file, one SQLite database: I wrote my monitoring tool

Aug 26, 2026 04:43 AM - 2 hours ago 3

July 9, 2026

I needed to watch a fleet of heterogeneous services: HTTP endpoints, PostgreSQL databases, a fewer Oracle instances, Redis, Elasticsearch indexes that must enactment fresh, machines that should reply ping, and immoderate Prometheus metrics. And I needed to beryllium told, connected Telegram, by SMS, connected Signal, erstwhile thing goes down, and erstwhile it comes back.

The classical reply is simply a monitoring platform: Prometheus positive Alertmanager positive Grafana positive a fistful of exporters, aliases a instrumentality moving a Node.js app pinch a database. All awesome tools. But for a fewer twelve checks, I did not want to run a 2nd distributed strategy conscionable to cognize whether the first 1 is up. And nary of the lightweight options could query Oracle without maine installing the Oracle customer libraries somewhere.

So I wrote Gjallar: a KISS monitoring service. One fixed binary, 1 YAML config file, 1 SQLite file. A black-and-red position page pinch history, HTMX-refreshed. About 3,400 lines of Go. MIT licensed.

Zero CGO, connected purpose

The full instrumentality builds pinch CGO_ENABLED=0:

CGO_ENABLED=0 spell build -trimpath -ldflags "-s -w"

That is only imaginable because each dependency that would traditionally hindrance to a C room has a pure-Go replacement nowadays, and they are excellent:

  • pgx for PostgreSQL: nary libpq;
  • go-ora for Oracle: no Oracle Instant Client, which unsocial justified the project. If you person ever deployed the Oracle customer connected a minimal box, you know;
  • pro-bing for ICMP echo, privileged aliases unprivileged;
  • modernc.org/sqlite for storage: SQLite transpiled to axenic Go, nary libsqlite3;
  • Redis needs nary driver astatine all: the cheque speaks the protocol directly: TCP connect, optional AUTH, PING, expect +PONG.

The consequence is simply a azygous self-contained binary (about 36 MB, astir of it the SQLite and Oracle drivers) that cross-compiles from my laptop to immoderate target pinch GOOS/GOARCH, and deploys pinch scp. No Docker, nary package manager, nary shared libraries, nary "works connected my machine".

A lock-free alert pipeline

Monitoring devices are people concurrent, each show waits connected the web astir of the time, and concurrency is wherever broadside projects usually turn their first mutex jungle. Gjallar has nary locks astir its authorities astatine all, because of really the pipeline is shaped:

one goroutine per show ──▶ results transmission ──▶ azygous consumer (state instrumentality + SQLite writes)

Each show runs its cheque loop successful its ain goroutine and sends check.Result values into a shared channel. A azygous user goroutine owns everything downstream: the up/down authorities machine, incident rows, and history writes. Since only 1 goroutine ever touches the authorities representation and the database connection, location is thing to lock, and SQLite, which dislikes concurrent writers, gets precisely one.

The per-monitor authorities is mini and explicit:

type monitorState struct { down bool consecFails int downSince time.Time lastNotified time.Time period int // consecutive failures earlier DOWN fires realert time.Duration // reminder interval while down; 0 = disabled notifiers []string }

Two creation points earned their support successful production:

  • State survives restarts. At startup, each monitor's authorities is seeded from immoderate unfastened incident successful SQLite. A restart while thing is down neither re-fires the DOWN alert nor misses the betterment notification. Deploying a caller type during an outage is simply a non-event.
  • Notifications are dispatched asynchronously. The user must ne'er block: a slow SMTP server aliases a rate-limited Telegram API cannot back-pressure the full pipeline. Sends spell retired successful their ain goroutines pinch a 15-second timeout.
  • Alerts occurrence aft N consecutive failures, not connected the first blip, nary flapping noise, and an optional realert interval reminds you while an incident stays open.

Configuration that respects operations

Everything lives successful 1 YAML file, pinch defaults, named notifiers, and show groups:

defaults: interval: 60s timeout: 10s failure_threshold: 3 alerts: [ops-telegram] alerts: ops-telegram: url: "telegram://TOKEN@telegram?chats=123456789" monitors: - name: app-db type: postgres dsn: "postgres://monitor:${PG_PASSWORD}@db1:5432/app" query: "SELECT count(*) FROM jobs WHERE position = 'stuck'" rule: "== 0"

Three mini features make it pleasant to operate:

  • Hot reload connected SIGHUP: systemctl reload gjallar applies the caller config, but only aft it has been fully validated. A surgery YAML keeps the moving configuration live and logs the error, alternatively of taking the monitoring down pinch it. Your watcher should beryllium the past point that dies from a typo.
  • ${VAR} situation description for secrets, pinch a clear startup nonaccomplishment if a referenced adaptable is undefined, and a bare $ (say, successful a ~ ^OPEN$ regex rule) near untouched.
  • A -check flag for dry-run validation, truthful CI tin lint the config earlier it ever reaches the server.

What it deliberately does not do

No clustering, nary agents, nary plugin system, nary time-series dashboards, nary personification accounts. History is pruned aft a configurable retention (30 days by default) truthful the SQLite record stays mini forever. If a request is served good by an existing elemental mechanism, systemd for the work lifecycle, shoutrrr URLs for the 20 notification services I will ne'er use, Gjallar delegates alternatively of reimplementing.

This is the portion I would take sides the hardest. Every monitoring instrumentality I person abandoned complete the years died of the aforesaid disease: it slow became a platform, and 1 time the monitoring needed monitoring. A instrumentality whose full authorities fits successful 1 SQLite record and whose full behaviour fits successful 1 YAML record is simply a instrumentality you still understand astatine 3 a.m., eighteen months aft you wrote it.

Code and documentation: github.com/brvier/Gjallar.

More