~/work/otel-collector

A custom OpenTelemetry Collector for 10,000 laptops.

Why we built our own Collector distribution instead of shipping the kitchen sink, how you safely update the thing that watches the fleet — and the hole I found in the Collector on macOS that ended up fixed upstream.

Client Platform Engineer (DevOps/SRE) · Splunk · OpenTelemetry, Go tooling (ocb), GitLab CI, Puppet, Splunk

10,000+
endpoints running the distribution
#33393
upstream proposal — macOS host metrics, since shipped
1
pipeline for health + compliance telemetry, vendor-neutral

context

The fleet’s SLOs needed eyes.

The fleet control loop only works if the telemetry underneath it is trustworthy. We needed host and compliance signals from every laptop in the company, and the usual answer — a pile of vendor agents, each with its own format, footprint, and bill — is how endpoint fleets end up running five overlapping daemons. OpenTelemetry offered the alternative: one collector, one pipeline, open schema, no lock-in on the backend.

constraints

The watcher must not hurt the watched.

A collector on a developer’s laptop lives under a resource budget — if the telemetry agent is the thing making the fans spin, you lose the room. macOS adds its own tax: everything must be signed and notarized, or Gatekeeper ends the conversation. And the hardest constraint is recursive: updating the agent that monitors the fleet, across the fleet, without blinding yourself while you do it.

design

A minimal distribution, built like software.

The OpenTelemetry Collector Builder (ocb) lets you compile a collector containing exactly the components you need and nothing else. Ours carried the host-metrics receiver, a small set of processors for batching and memory protection, and the Splunk exporter — a deliberately boring manifest. The manifest lived in git; CI built, signed, and packaged it; Puppet rolled it out cohort by cohort, watching the same SLIs the collector itself reports. When the telemetry from a canary cohort stays healthy, the wave continues.

Telemetry pipeline and ship path Top row: the telemetry pipeline from the host-metrics receiver through batch and memory-limiter processors to the Splunk exporter. Bottom row: the ship path from the ocb manifest through GitLab CI signing to packages rolled out to fleet cohorts by Puppet. the pipeline hostmetrics receiver · CPU, disk, mem processors batch · memory_limiter exporter Splunk · OTLP the ship path manifest.yaml ocb · in git GitLab CI build · sign · package fleet cohorts Puppet · canary → wave
The collector’s telemetry pipeline and its deployment path.

the darwin gap

The metrics that weren’t there.

Rolling this out surfaced an uncomfortable fact: the Collector’s host-metrics receiver couldn’t report CPU or disk metrics on macOS — the scrapers needed CGO and simply weren’t supported on Darwin. On a fleet that was heavily macOS, that’s not a footnote; it’s a hole in the middle of the health model.

The fork was the tempting answer — patch it privately, ship it, move on. But a private fork of a telemetry agent is a maintenance debt you pay forever, on every upstream release. So I took it upstream instead: I authored the proposal (opentelemetry-collector-contrib #33393) making the case for Darwin CPU/disk support with CGO, pushed for it internally and in the issue, and dug into the receiver’s code to understand what the fix required. The implementation PR that landed was authored by the component’s maintainers — the feature ships in the Collector today, and I’ll take that outcome. Upstream is a contact sport: an earlier PR of mine to Installomator, the Mac-admin install framework, got beaten to the merge by a parallel fix after ten commits of work. You file anyway. The commons is how you delete work forever.

$ otelcol query host.cpu.utilization   # 2024, darwin
no data — scraper unsupported on this platform
$ otelcol query host.cpu.utilization   # after #33393
host.cpu.utilization  0.42  {cpu="0", state="user"} 
$ 

lessons

What I’d tell the next fleet.

  • Upstream-first beats forever-fork. Filing the issue was a one-time cost; a private fork would have been a bill due on every upstream release, forever.
  • Minimal distributions win. Every component you don’t compile in is attack surface, memory, and upgrade risk you don’t carry.
  • Collector config is code. The manifest and config lived in git and went through CI like everything else — telemetry outages from hand-edited configs are self-inflicted.

outcomes

One pipeline, all the way down.

The distribution ran on 10,000+ endpoints and fed every SLI in the fleet control loop. macOS host metrics now ship in the upstream Collector for everyone. The same discipline runs this very website — live SLOs, real-user vitals, and an error budget you can inspect — and carried into the security product I built on my own time: Copywarden →