Savinho isn't just another observability tool-it's a 200KB Golang binary that saturates your telemetry pipeline with eBPF-powered, zero-instrumentation traces.

In production environments, we often wrestle with agents that either demand heavy SDK integration or guzzle memory like a misconfigured JVM. When our platform team first encountered Savinho, it felt like discovering tcpdump's spiritual successor for the cloud-native era. The project-born from a Brazilian SRE collective frustrated with sidecar overhead-has quietly been reshaping how mid-size engineering organizations approach distributed tracing and metrics gathering, all while respecting the budget constraints of lean ops teams.

This article is a practitioner's deep look at Savinho's architecture, its eBPF internal, the OTLP-native data flow. And the pragmatic tradeoffs we've cataloged after running it across three Kubernetes clusters serving 80+ microservices. You'll come away understanding where Savinho fits, how it differs from the incumbent Jaeger/OpenTelemetry Collector combo. And why its approach to kernel-level instrumentation might nudge you to rethink your current observability stack.

Server racks in a data center, representing infrastructure monitored by Savinho agent

What Exactly Is Savinho? A New Breed of Observability Agent

Savinho is an open-source, single-binary observability agent written entirely in Go. Unlike the OpenTelemetry Collector (which can function as an agent or gateway), Savinho is purpose-built to run as a DaemonSet on every node or as a lightweight sidecar. Its core differentiator: it uses eBPF to capture HTTP/gRPC spans and network metrics without requiring application code changes, SDK libraries. Or even service mesh sidecars. The first time we deployed it on a staging node, we watched Kafka client latency traces appear in Grafana within seconds of applying the DaemonSet manifest-no code shipped, no restarts.

The project's design philosophy is distilled into three principles: invisible instrumentation, minimal resource footprint, vendor-agnostic exporting. Savinho ingests no custom protocols; it observes kernel events through eBPF programs attached to kprobes, tracepoints, and uprobes, then transforms them into OpenTelemetry's OTLP format. This means any backend that speaks OTLP-Grafana Tempo, Honeycomb, Datadog. Or a homegrown collector-can consume Savinho's output natively. The day we pointed it at a ClickHouse-backed Tempo instance, the engineering team's Slacks lit up with "who instrumented the auth service? "-nobody had; Savinho just saw the TCP connections and reconstructed the spans.

Under the hood, Savinho relies on the cilium/ebpf Go library to load BPF programs, and its OTLP exporter leverages the official OpenTelemetry Go SDK's OTLP exporterThe project maintainers have taken care to separate the data plane (eBPF collection) from the control plane (export scheduling, sampling decisions), keeping the critical path in eBPF maps for near-zero overhead.

The Architectural Decision: Why Golang and gRPC?

Choosing Go for Savinho wasn't merely a matter of "the founders like it. " The agent needs to handle thousands of eBPF events per second, marshal them into protobuf structures. And batch-export them over gRPC-all while staying under 50MB of resident memory. Go's goroutine model maps elegantly to this pipeline: a dedicated goroutine reads from a ring buffer containing raw trace events, several worker pools convert them to OTel spans. And a final exporter goroutine manages connection pooling via gRPC's HTTP/2 streaming. In our load tests, Savinho's CPU usage stayed below 0. 3 vCPU on a node serving 2000 requests/second.

The adoption of gRPC for telemetry export-rather than Thrift (Jaeger's legacy) or plain HTTP/JSON-delivers notable efficiency gains. Savinho's exporter compresses OTLP payloads with gzip before sending them over a persistent connection, reducing bandwidth by 60% compared to uncompressed JSON. We validated this using tcpdump captures on our staging cluster and reproduced it with a simple pprof memory diff: the protobuf marshaling accounted for only 4% of total CPU cycles even under heavy load. If you're curious, the OTLP specification codifies the wire format that Savinho implements with strict adherence.

Moreover, Go's cross-compilation lets the Savinho maintainers ship static binaries for amd64 and arm64 with a single Makefile command. That meant we could run identical agent versions on our x86 staging nodes and our Graviton-based production nodes without linker dance. The binary is about 12MB uncompressed-an order of magnitude smaller than the default OTel Collector bundle, which includes every available receiver and exporter.

Close-up of a circuit board with glowing traces, evoking low-level kernel instrumentation

eBPF-Powered Instrumentation Without Source Code Changes

The real magic of Savinho lives in its eBPF programs. Rather than relying on language-specific auto-instrumentation agents (which often lag behind language versions or require specific library versions), Savinho attaches to kernel functions like tcp_sendmsg, security_socket_recvmsg, and libc's SSL_write via uprobes. It examines the socket buffer data to extract HTTP method, URL, status code, and headers-all without parsing application memory directly. during a proof-of-concept on our monolithic Django app (which we couldn't readily upgrade to OpenTelemetry), Savinho captured 99. 3% of the requests we manually validated via Nginx logs.

One nuance engineers quickly discover: Savinho can't auto-instrument message queue consumers or in-process database calls without additional uprobe definitions. For Postgres wire protocol, the project provides optional eBPF probes that parse the frontend/backend protocol; for Redis, a separate module reconstructs commands from RESP serialization. The community maintains a probe registry where teams share custom probes for niche protocols. In our case, we contributed a probe for Aeron transport after noticing gaps in our low-latency trading pipeline traces-the PR was reviewed and merged within a week.

Because eBPF programs run in a sandboxed verifier, Savinho is safe to deploy even in hardened environments like Bottlerocket or Flatcar Linux. The verifier ensures no out-of-bounds memory accesses. And the agent runs with minimal Linux capabilities (CAP_BPF and CAP_PERFMON). We spent a weekend stress-testing it with the Kernel Self Protection Project's test suite and found zero panics or verifier rejections-a shows the care taken in the BPF code. Related: our guide on kernel security for observability agents

Distributed Tracing with Savinho: W3C Trace Context in Action

To stitch eBPF-captured spans into a coherent distributed trace, Savinho relies on the W3C Trace Context standard. When an HTTP request enters the kernel, the agent parses the traceparent and tracestate headers from the socket data and propagates them into its internal context. If no trace context exists, Savinho can generate a new one using a configurable ID generator (random. Or X-Ray compatible). In a mixed environment where some services do have native OpenTelemetry SDKs, Savinho effectively bridges the gap, allowing end-to-end traces that start with an unmodified legacy service and end in a modern Go service instrumented with the OTel SDK.

We observed a subtle edge case with gRPC streaming calls: the initial headers frame carries the trace context. But Savinho must track both the initial and subsequent frames to correctly attribute server-side spans. The project solved this through a state machine inside the eBPF program that follows the gRPC framing protocol as defined in RFC 7540 and gRPC's own HTTP/2 transport. Our team verified correct linkage by injecting synthetic delays and examining Tempo trace waterfalls-the parent-child relationships matched exactly what our client-side SDKs reported.

Sampling decisions happen at the agent level, not in the kernel. Savinho supports head-based sampling (probabilistic or rate-limiting) that writes decisions into a per-trace eBPF map. This means the kernel only captures fully sampled traces, reducing overhead dramatically. In our default 10% sampling configuration, production nodes saw only a 0. 1% throughput reduction, even under spiky traffic. Tail-based sampling is planned for a future release via a sidecar component. But the head-based approach has been sufficient for our anomaly detection needs.

Metrics Collection and Aggregation: A Time-Series Perspective

While tracing grabs the limelight, Savinho doubles as a lightweight metrics agent. It exposes a /metrics endpoint in Prometheus text format and also pushes aggregated metrics to OTLP-compatible backends. The eBPF programs collect request counts, latencies (as histograms). And error rates directly from the network stack, offering RED (Rate, Errors, Duration) metrics without needing instrumentation libraries. We compared its latency histograms to those from our Envoy sidecars-the 95th percentile diverged by less than 0. 8ms, comfortably within the noise floor.

For more detailed application-level metrics (CPU user time, memory allocations), Savinho can optionally load a minimal eBPF CPU profiler that samples stack traces. This capability is still experimental. But we've used it to identify a goroutine leak in a Go service that had evaded our traditional pprof surveillance. The profiler writes flamegraph-compatible data to an OTLP span attribute, enabling correlation between high-latency requests and specific functions in the code. See our article on continuous profiling with Parca

One design choice worth noting: Savinho doesn't support long-term metric storage; it's a push agent, not a database. That's a deliberate separation of concerns. It means you can pair it with VictoriaMetrics, Thanos, or Prometheus without coupling to a specific TSDB. The OtelMetric exporter batches metrics every 15 seconds by default, configurable via the --metrics batch-interval flag.

Custom Exporters and the OpenTelemetry Protocol (OTLP) Pipeline

Savinho's pipeline follows a classic fan-out pattern. Raw eBPF events enter an internal channel, are transformed into OTel pdata objects (using the OpenTelemetry Collector's pdata module). And then fan out to multiple exporters. Besides OTLP, the agent ships with exporters for Jaeger Thrift, Zipkin v2 JSON, and even a debug stdout exporter. This modularity made it trivial for us to write a custom exporter

.

Need a Custom App Built?

Let's discuss your project and bring your ideas to life.

Contact Me Today โ†’

Back to Online Trends