a prometheus-compatible metrics server in ~6,500 lines of go

Metrics you can read end to end.

PromQL verified against Prometheus's own test suite, a Grafana-ready API, remote write, and a storage engine with real cardinality and retention limits. One binary. No cluster.

~/tinyobszsh
$ go run ./cmd/server
level=INFO msg="TinyObs started" retention=72h max_series=50000
$ curl -s localhost:8080/api/v1/query \
    --data-urlencode 'query=topk(3, sum by (path) (
        rate(http_requests_total[5m])))' \
    | jq -r '.data.result[] | "\(.metric.path)\t\(.value[1])"'
/api/users/{id}    41.2
/api/orders        17.9
/healthz            4.0
$ curl -s localhost:8080/metrics | grep rejected
tinyobs_samples_rejected_total{reason="metric_series_limit"} 1204
$ 
sum(rate(http_requests_total[5m]))last 1h
14:0014:1514:3014:4515:00
01

What's inside

The whole system, by package. Lines of Go, not counting tests, comments or blank lines. Every piece is small enough to read in one sitting.

promql2,820Lexer, parser and evaluator, checked against upstream promqltest
sdk1,266Go client: counters, gauges, histograms, HTTP middleware
tsdb920BadgerDB storage, label index, retention, cardinality limits
api725Prometheus HTTP API and remote write
server484Wiring, background retention, /metrics self-monitoring
labels345Label sets, matchers, validation
total6,560Prometheus is roughly 50× larger.
02

Guardrails, not surprises

The failure modes that take down real metrics systems are handled up front, and every one of them is observable.

Cardinality

A global series cap plus a per-metric cap, so one label explosion is rejected on its own instead of starving every other metric. The SDK labels requests by route pattern and collapses 404s.

TINYOBS_MAX_SERIES_PER_METRIC=10000

Retention

Time-based, applied hourly, with disk that stays predictable. Samples compress to about 14 bytes each on BadgerDB with ZSTD.

disk ≈ series × samples × 14 B

Nothing dropped silently

Every rejected sample is counted by reason and exposed on /metrics. The heaviest metrics and labels are one request away.

tinyobs_samples_rejected_total{reason}
03

Where it fits

TinyObs trades horizontal scale for something you can hold in your head.

use it for

  • +Local development and staging
  • +Side projects and single-host services
  • +Learning how a TSDB and PromQL actually work
  • +Performance checks in CI

not for

  • −Highly available production monitoring
  • −Multi-tenant workloads
  • −Long-term storage

→ use Prometheus, Thanos or Mimir