Skip to content

See what broke without operating an observability stack.

Send traces, logs and metrics to one process over OpenTelemetry. Investigate incidents in the browser or through an agent, and run alerts — without operating the stack behind them.

One Go process: OTLP in over gRPC or HTTP, authoritative Parquet on disk, indexed trace reads, and embedded DuckDB analytics. No database to operate.

checkoutp95 latency · last 1h
13:2014:22now
p95412ms+38%
errors2.4%+0.2
rps1.2kstable

why is checkout slow right now?

missing index on orders(user_id) — 88% of slow spans block on db-orders

  • Self-hosted on your hardware

  • OTLP native traces, logs and metrics

  • Open local storage authoritative Parquet, embedded queries

  • One small VM reference deployment target

  • Apache-2.0 open source

What you look at

Every service, one screen, no query language.

Section titled “Every service, one screen, no query language.”

Health, latency, error rate and throughput for everything reporting — with the slow one already at the top. The chat investigator works from the same typed queries this view does, so what it tells you and what you are looking at cannot disagree.

demo.fanout.run · Overviewlast 1h

Services

ServiceHealthp95ErrorsRps
checkoutunhealthy412 ms+38%2.4%1.2k
cartdegraded187 ms0.6%2.1k
cataloghealthy42 ms0.0%3.8k
paymenthealthy94 ms0.1%980
authhealthy28 ms0.0%5.4k

5 of 47 services · worst first

Health

Score86+2 vs 1h
Spans/min2.1Mstable
Errors0.4%+0.2
Services472 alerting

Throughput

What it replaces

This is the whole argument. Every other claim on this page follows from it — the short backup procedure, the single configuration, and the absence of an integration layer to debug at three in the morning.

The usual arrangement

  • CollectorReceive OTLP, batch, forward
  • Storage backendPersist telemetry, expire it
  • Query layerAnswer questions over that store
  • Dashboard serviceRender it for people
  • AlertmanagerEvaluate rules, deliver notifications

Five deployments, five configurations, five upgrade cadences — and the failure modes that exist only between them.

Fanout

  • OTLP in over gRPC and HTTP
  • Authoritative Parquet on local disk
  • Indexed traces and DuckDB analytics
  • The browser client, served by the binary
  • The alert engine, on a fixed interval

One binary to pin, one directory to back up, one process to restart.

Inside the binary

Three signals in, one process, answers back out.

Section titled “Three signals in, one process, answers back out.”

Fanout is where your telemetry lands, not something you put in front of the thing it lands in. Traces, logs and metrics arrive over standard OTLP from SDKs or a Collector you already run, and everything after that happens inside one binary.

Traces, logs and metrics arrive over OTLP into a single Fanout process that publishes authoritative Parquet with trace indexes and uses DuckDB for analytics, and the answers leave to a browser, the HTTP API, or an agent over MCP. traceslogsmetricsbrowserAPIagent · MCPone processParquet + trace indexesDuckDB analyticsOTLP

Nothing in the middle crosses a network, and nothing else has to be running for it to answer. The data directory it writes is the whole of its state.

Measured on one machine

Nearly 300,000 OpenTelemetry items a second. One binary.

Section titled “Nearly 300,000 OpenTelemetry items a second. One binary.”

In a five-minute publication-candidate run, Fanout accepted 296,196 spans, log records and metric data points per second on a machine with eight logical CPUs and 15.6 GiB of memory. Fanout reported zero dropped rows, the process did not restart, and the resulting Parquet store passed verification. The load generators shared the machine, so this is an observed whole-host result—not an isolated storage-engine ceiling.

Publication candidatemeasured on one machine · methodology

Sustained three-signal ingest

296,196 items/s

Fanout's accepted-row counter over five minutes—not the load generator's requested rate.

Accepted throughputitems/s
Accepted three-signal throughput by generator process countThroughput rose from 38,853 items per second with one generator process per signal to a 355,938 short peak with sixteen. The five-minute sustained rate at sixteen processes, the headline figure, was 296,196. The horizontal axis doubles at every step, so its points are evenly spaced.400k300k200k100k0124816generator processes per signal · doubling, evenly spaced355,938/s short peak296,196/s sustained over five minutes

Each signal was also saturated on its own. These three runs are separate measurements, not a breakdown of the sustained three-signal rate above.

Traces alone220,324spans/s
Logs alone187,303records/s
Metrics alone209,638points/s
Whole suite153.8Mitems accepted, cumulative
server drops 0no restartsParquet verified

v2026.8.11 · 8 logical CPUs · 15.6 GiB · same-host generators · OTLP/gRPC

It is labeled a candidate because it is a single run: the published headline waits on a clean harness repeating it three times. Read the methodology and every qualification, or run the same open-source harness on your own host.

Limits

These decide whether Fanout fits, so they are here rather than three pages in. None of them is a roadmap item phrased as a virtue.

  • Not multi-tenant: namespaces scope queries, not access. Anyone who can query the instance can query every namespace in it; an instance is one trust domain.
  • No horizontal scale: ingest, query and maintenance share a process and a machine. There is no scaling one without the others, because they are the same thing.
  • No universal capacity number: the benchmark above describes one release, host and workload. Your payload cardinality, query mix and disk decide the capacity of your instance. Measure your own hardware.
  • No migration paths: Fanout is pre-release. A renamed setting is renamed, not aliased, which is why an unknown variable stops the process rather than being ignored.

Install it and point something at it.

One binary, one data directory. The installer checks the archive against the release checksums before it extracts anything.

curl -fsSL https://raw.githubusercontent.com/labstack/fanout/main/scripts/install.sh | sh