Documentation
Everything needed to run TinyObs, get metrics into it, and query them back out.
01Quick start
TinyObs is one Go binary with an embedded store. There is nothing else to run. The dashboard and API both listen on port 8080.
git clone https://github.com/nicktill/tinyobs && cd tinyobs docker compose up -d
go run ./cmd/server
To generate demo traffic, run the example app with go run ./cmd/example or make docker-demo, then open http://localhost:8080.
02Instrument an app
The Go SDK batches samples and pushes them every few seconds. The HTTP middleware records http_requests_total and http_request_duration_seconds, labelled by the route pattern your ServeMux matched, so IDs in URLs never become new series.
client, _ := sdk.New(sdk.ClientConfig{
Service: "my-app",
Endpoint: "http://localhost:8080/v1/ingest",
})
client.Start(ctx)
defer client.Stop()
mux := http.NewServeMux()
mux.HandleFunc("GET /users/{id}", getUser)
handler := httpx.Middleware(client)(mux) // path="/users/{id}"
errors := client.Counter("errors_total")
errors.Inc("type", "api_error")03Remote write
Anything that speaks Prometheus remote write 1.0 can push to TinyObs: Prometheus, Grafana Alloy, the OpenTelemetry Collector, vmagent.
remote_write: - url: http://localhost:8080/api/v1/write
If TINYOBS_AUTH_TOKEN is set, add authorization: { credentials: <token> } to the entry. The Go SDK sends it from ClientConfig.APIKey.
Samples that can never be stored (out of order, over a series limit, invalid labels) get a 400, so senders drop them instead of retrying forever. Storage errors get a 5xx and are retried.
04PromQL
The engine runs Prometheus's own promqltest suite (make promql-compat). A supported feature returns what Prometheus returns. Anything else is an explicit “not supported” error, never a guess.
supported
- Selectors with
=!==~!~, range vectors,offset, subqueries - Arithmetic, comparison (with
bool) and set operators withon,ignoring,group_left,group_right sum avg min max count group stddev stdvar topk bottomk quantilerate irate increase delta idelta deriv predict_linear resets changes*_over_time,histogram_quantile, math, clamping, date and label functions
not supported
- Native histograms
- The
@modifier and duration expressions count_values,limitkholt_winters,sort_by_label
curl -s localhost:8080/api/v1/query \ --data-urlencode 'query=histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))'
05HTTP API
Request parameters, response shapes and error types follow the Prometheus HTTP API.
| endpoint | purpose |
|---|---|
/api/v1/query | Instant query |
/api/v1/query_range | Range query |
/api/v1/series | Series matching selectors |
/api/v1/labels | Label names |
/api/v1/label/<name>/values | Values of one label |
/api/v1/metadata | Metric type and help text |
/api/v1/status/tsdb | Series count and the heaviest metrics and labels |
POST /api/v1/write | Prometheus remote write 1.0 |
POST /v1/ingest | Go SDK ingest |
/metrics | TinyObs's own metrics |
/-/healthy, /-/ready | Health checks |
06Configuration
Configured with environment variables. Invalid values stop the server at startup rather than falling back to a default.
| variable | default | description |
|---|---|---|
TINYOBS_LISTEN | 127.0.0.1:8080 | Listen address. Loopback by default; the Docker image uses 0.0.0.0:8080 |
PORT | 8080 | Port, when TINYOBS_LISTEN is unset |
TINYOBS_AUTH_TOKEN | unset | Bearer token required on the write endpoints |
TINYOBS_DATA_DIR | ./data/tinyobs-v2 | Data directory |
TINYOBS_RETENTION | 72h | How long samples are kept |
TINYOBS_MAX_SERIES | 50000 | Total series limit |
TINYOBS_MAX_SERIES_PER_METRIC | 10000 | Series limit per metric name |
TINYOBS_MAX_MEMORY_MB | 64 | Storage memory budget |
07Cardinality & retention
Every unique label set is a series, and series are what cost memory and disk. TinyObs caps them twice. The global limit bounds the whole server. The per-metric limit stops one runaway label, such as a user ID, from using up the global budget for every other metric.
Pushed series have no scrape to fail. When a service stops, its series keep their last value for the 5-minute lookback window, then drop out of instant queries.
Retention is time-based and runs hourly, without pausing ingestion. Disk use is predictable:
disk ≈ series × samples per series in the window × ~14 bytes
# 5,000 series, 15s interval, 72h retention
5,000 × 17,280 × 14 B ≈ 1.2 GBRejected samples are never dropped silently. Alert on the counter, and use the status endpoint to find the metric responsible:
curl -s localhost:8080/metrics | grep -E 'tinyobs_(series|samples_rejected)' curl -s localhost:8080/api/v1/status/tsdb | jq '.data.seriesCountByMetricName'
08Grafana
Add a Prometheus data source with the URL http://localhost:8080 (from a container, http://host.docker.internal:8080). Existing dashboards work, as long as their queries stay within the supported PromQL above.