DOSSIER 002 · FIELD NOTES
Kestrel: infrastructure monitoring in Rust, with mTLS from the start
A small agent and a single server that aim to replace Zabbix and Nagios with something quick to install and safe to put on the internet.
Kestrel is open-source infrastructure monitoring written in Rust. There's one small agent and one server. No PHP, no Perl. The goal is to replace Zabbix and Nagios with something you can install in five minutes and trust on the internet.
Why I built it
Zabbix and Nagios run a huge share of the world's monitoring, and they've earned that. They also show their age. Setup is heavy, the web UIs are dated, agents often talk in plaintext, and the C codebases have long CVE histories.
I didn't want to throw out the parts people like. Agents, templates, triggers and Nagios plugins all stay. What I wanted to add is the stuff you now expect by default: mutual TLS between agent and server, Prometheus and OpenTelemetry compatibility, multi-tenancy for MSPs and a modern web UI.
How it works
The workspace has five crates: a shared wire protocol, the collectors and check runners, the agent, the axum server and an admin CLI. The web UI is a separate Svelte app.
The agent is a single binary for Linux, Windows and macOS. Its collectors cover CPU, memory, disk I/O, filesystems, network, processes, services, uptime, log tailing and HTTP checks. It can scrape and expose the Prometheus format, and run Nagios plugins and your own scripts. That last part is locked down: remote commands are off by default, only executables inside the configured plugin directories can run, paths are canonicalised, and the agent never uses a shell.
Enrolment is one step. You create a short-lived token on the server:
kestrel token create --ttl 1h
The agent uses it once to enrol with the server's internal CA, then pushes samples over mTLS and renews its own certificate. Only the token's hash is stored. Batches are protobuf compressed with zstd, and if the server is unreachable the agent buffers to disk until it's back. Every 60 seconds it asks the server for its desired config, so you change templates in one place.
On the server, time series go to VictoriaMetrics and config lives in PostgreSQL. Triggers are PromQL expressions evaluated at least every 15 seconds, with a state machine per trigger and host that moves from OK to pending to problem to resolved. Here are two from the docs:
kestrel_filesystem_used_ratio > 0.9
kestrel_tls_cert_expiry_timestamp_seconds - time() < 14 * 24 * 3600
There's a built-in trigger for agents that go quiet for three minutes. Actions can have delayed escalation steps, which are skipped once someone acknowledges or the problem resolves. Maintenance windows can be a single interval or a weekly rule evaluated in the window's own timezone, so a local time survives daylight saving. Events can also be forwarded to a SIEM over syslog or webhook.
Where it's at
It's pre-alpha. The agent, enrolment and mTLS, the PostgreSQL schema, ingest, the API, triggers and the operator UI are in place and covered by workspace tests. Some of the server's long-running background loops aren't switched on yet. The roadmap runs from an MVP through Zabbix-level parity (templates, discovery, SNMP, escalations, RBAC) to multi-tenancy, OTLP ingest, synthetic checks and high availability. Treat it as something to try in a lab for now, not something to run your production on.
Try it
The quick start uses Docker for the backing services:
cd deploy/dev
docker compose up -d postgres victoria mailpit
docker build -f Dockerfile.server -t kestrel-server .
Run the server container against that stack, create the first admin with kestrel-server admin create, sign in to the UI, then enrol an agent with a token. Mailpit catches any alert email on port 8025. Full steps are in the quick start.
The code is MIT licensed at github.com/jusso-dev/kestrel.