Keen-Kestrel

Keen-Kestrel

Docker app from Ferdinand99's Repository

Overview

Keen Kestrel is self-hosted error tracking and privacy-friendly analytics in one small Rust binary with SQLite: no Kafka, ClickHouse, Redis or Postgres. Errors: point the official Sentry SDKs (Swift/iOS, JavaScript, Python, Rust, Go and more) at Kestrel's DSN. Events are scrubbed of personal data and grouped into issues with search, stack traces, resolve/ignore/merge, alerts to ~40 notification services, release health (crash-free sessions and users), and iOS/macOS crash symbolication from dSYMs uploaded with sentry-cli. Analytics: cookieless web analytics with the 1 KB k.js snippet, and app signals from the KestrelKit Swift package. No IP addresses or user agents are stored, and Global Privacy Control is respected. Also: a REST API with OpenAPI, MCP tools for coding assistants, users with roles and two-factor login, 26 languages. Setup: open the WebUI after the first start and create the admin account. Set KK_PUBLIC_URL to the address your apps and sites use (it goes into DSNs, the k.js snippet and alert links). Behind a reverse proxy, set KK_TRUST_PROXY to 1. Everything lives in two SQLite files under /data (appdata).

Keen Kestrel

Self-hosted error tracking and privacy-friendly analytics in one small binary.

License: MIT Website

Status: v0.2 "analytics", an early release (Docker image only). Error tracking works end to end in 26 languages: the official Sentry SDKs send events, which are scrubbed of personal data, grouped into issues and stored; issue and event pages with search, live updates, stack traces and triage; iOS and macOS crashes symbolicated from dSYMs uploaded with sentry-cli; alerts for new issues, regressions, spikes and a release's first crash to ~40 notification channels (Slack, Discord, Teams, Telegram, ntfy, email, webhooks …); release health with crash-free sessions and users; and a REST API, OpenAPI document and MCP tools for all of it. Web analytics: a script under 2 KB, no cookies and no stored IP addresses, a Web tab with visitors, pageviews, pages, referrers, UTM, browsers and devices, filters and live current visitors, and it respects Global Privacy Control. App analytics with the KestrelKit Swift package: an App tab with daily, weekly and monthly active users, sessions, signal types, version adoption, OS versions, devices and locales, a signal explorer for payloads, and each release's adoption next to its crash-free rate. Installers and Unraid/Proxmox templates come by v1.0. The translations are unreviewed drafts. Sending errors to Kestrel shows how to point each SDK at it, upload dSYMs and set up alerts; Web analytics shows how to add the snippet and what is kept; App analytics does the same for KestrelKit. The design is in docs/design.md; to build and run it, see docs/development.md.

Keen Kestrel answers two questions about the apps and sites you run:

  • What broke? Errors and crashes from the official Sentry SDKs (iOS/Swift, JavaScript, Rust, Python, Go and more) work unchanged. Issues are grouped, alerts go to the same ~40 channels as Vigilant Vole, and iOS crashes are symbolicated from the dSYMs that sentry-cli uploads.
  • How is it used? App signals and website pageviews without cookies or stored IP addresses, with dashboards for users, versions, devices, pages and referrers.

One Rust binary and SQLite: no Kafka, ClickHouse, Redis or Postgres. It shares its design language and foundations with Vigilant Vole and runs on Docker, Unraid, Proxmox or plain Linux.

Install

Kestrel runs as one container with its data in a single folder:

mkdir keen-kestrel && cd keen-kestrel
curl -fsSLO https://raw.githubusercontent.com/Ferdinand99/Keen-Kestrel/main/docker-compose.yml
docker compose up -d

Then open http://<host>:3002/setup to create the first admin, create a project and copy its DSN. Set the address your apps use under Settings → General (public URL), or as KK_PUBLIC_URL in docker-compose.yml, which is stored there at every start: it goes into the DSNs and alert links. Put Kestrel behind HTTPS with KK_TRUST_PROXY=1 when it's reachable from the internet. Images are on GHCR for amd64 and arm64: ghcr.io/ferdinand99/keen-kestrel:<version> or :latest.

Planned for v1

Errors Analytics
Sentry-compatible ingest (envelopes, sessions, feedback, attachments) App signals via the KestrelKit Swift package
Issue grouping, search, resolve/ignore/merge, regressions Web pageviews via a script under 2 KB
iOS/macOS symbolication from dSYMs (sentry-cli compatible) Daily, weekly and monthly users, versions, devices, locales
Release health: crash-free sessions and users Pages, referrers, UTM, browsers
Alerts: new issue, regression, spike, first crash in a release No cookies, no stored IPs, daily-rotating salts
REST API, OpenAPI, MCP tools for coding agents Same projects, keys and releases as errors

Not in v1: performance tracing, session replay, JavaScript source maps (planned for v1.1), funnels and cohorts. See docs/design.md for the full scope and the phases.

License

MIT

Requirements

Early release (v0.1). To receive events from phones and websites, Kestrel must be reachable from the internet over HTTPS, for example through a reverse proxy or tunnel; set KK_PUBLIC_URL to that address.

Related apps

Details

Repository
ghcr.io/ferdinand99/keen-kestrel:latest
Last Updated2026-10-23
First Seen2026-10-23

Runtime arguments

Web UI
http://[IP]:[PORT:3002]/
Network
bridge
Shell
sh
Privileged
false

Template configuration

WebUI portPorttcp

Web interface, API, ingest (Sentry SDKs, KestrelKit, k.js) and sentry-cli uploads.

Target
3002
Default
3002
Value
3002
DataPathrw

kestrel.sqlite, store.sqlite, uploaded debug files, session key and pre-migration backups. Ownership is set to PUID:PGID on every start.

Target
/data
Default
/mnt/user/appdata/keen-kestrel
Value
/mnt/user/appdata/keen-kestrel
KK_PUBLIC_URLVariable

The public address apps and sites use, e.g. https://kestrel.example.com. Used in DSNs, the k.js snippet and alert links. Without it, each request's own address is used.

KK_TRUST_PROXYVariable

Set to 1 when Kestrel is reached through a reverse proxy or tunnel (SWAG, Nginx Proxy Manager, Traefik, Pangolin) that sets X-Forwarded-* headers.

Default
0
Value
0
KK_SETUP_TOKENVariable

Optional. When set, the first-run /setup page only opens with ?token=VALUE, so nobody else can create the admin account on an exposed instance.

Ingest port (optional)Variable

Optional second port inside the container that serves only ingest and /healthz, so the admin UI can stay on the LAN. Also map that port below if you use it.

Target
KK_INGEST_PORT
PUIDVariable

User id the app runs as (Unraid: 99 = nobody).

Default
99
Value
99
PGIDVariable

Group id the app runs as (Unraid: 100 = users).

Default
100
Value
100
TZVariable

Time zone for log timestamps.

Default
Europe/Oslo
Value
Europe/Oslo
RUST_LOGVariable

Log level filter.

Default
info
Value
info