Alloy vs OpenTelemetry Collector

Alloy is Grafana’s distribution of the OpenTelemetry Collector with its own configuration language and a native Prometheus/Loki/Pyroscope pipeline next to the OTel one.

In the otelcol.* layer both run the same upstream Go code. Comparing their CPU or memory for OTLP processing measures noise.

Same engine, different chassis

The real differences are:

  • Component catalog — which receivers, processors and exporters exist.
  • Lifecycle — how config is loaded, reloaded and managed across a fleet.
  • Operations tooling — UI, clustering, remote config.

Feature comparison

Feature OpenTelemetry Collector Grafana Alloy
Project CNCF, vendor-neutral Grafana Labs, Apache 2.0
Config YAML; wiring in service.pipelines Alloy syntax; wiring by references
Signals Traces, metrics, logs (profiles in development) Traces, metrics, logs, profiles (pyroscope.*)
Prometheus scraping prometheus receiver (converts to OTLP) Native prometheus.scrape, ServiceMonitor/PodMonitor CRDs (prometheus.operator.*)
Component catalog Full contrib (hundreds of components); ocb builds a minimal binary Curated subset of contrib wrapped as otelcol.*, plus Alloy-only components
Stability gating Per component (feature gates) Per process (--stability.level)
Reload Restart (or OpAMP Supervisor) /-/reload; local.file, remote.kubernetes.secret, remote.vault re-evaluate live
Remote config OpAMP + Supervisor remotecfg block, Grafana Fleet Management
Clustering None built in; shard with the Target Allocator (Operator) Built in: gossip + consistent hashing of scrape targets
Debug UI None (zPages, logs) Component graph, health, live debugging on :12345
Reusable modules declare, import.git, import.file
Config conversion alloy convert --source-format=... (otelcol, prometheus, promtail) — one way, into Alloy

Components that exist in contrib but not in Alloy v1.16.1 (checked with alloy validate): otelcol.exporter.elasticsearch, otelcol.connector.routing. Both loadbalancing and tail_sampling are available.

The same pipeline in both

OpenTelemetry Collector (YAML):

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: "0.0.0.0:4317"

processors:
  memory_limiter:
    check_interval: 1s
    limit_percentage: 80
    spike_limit_percentage: 20
  batch:
    timeout: 5s

exporters:
  otlp:
    endpoint: "tempo:4317"
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlp]

Grafana Alloy:

otelcol.receiver.otlp "default" {
  grpc {
    endpoint = "0.0.0.0:4317"
  }
  output {
    traces = [otelcol.processor.memory_limiter.default.input]
  }
}

otelcol.processor.memory_limiter "default" {
  check_interval         = "1s"
  limit_percentage       = 80
  spike_limit_percentage = 20
  output {
    traces = [otelcol.processor.batch.default.input]
  }
}

otelcol.processor.batch "default" {
  timeout = "5s"
  output {
    traces = [otelcol.exporter.otlp.tempo.input]
  }
}

otelcol.exporter.otlp "tempo" {
  client {
    endpoint = "tempo:4317"
    tls {
      insecure = true
    }
  }
}

Field names drift between the two: limit_mib in YAML is limit (a size string) in Alloy, and spike_limit_mib is spike_limit.

Decision table

Situation Pick Why
You need a component Alloy does not wrap (Elasticsearch exporter, routing connector, a niche receiver) Collector The only hard technical argument
“GA only in production” policy, but one pipeline needs a preview component Collector Alloy’s --stability.level lowers the bar for the whole process; the Collector gates per component
You patch config with kustomize / yq, or template it from another system Collector Plain YAML
Minimal image, minimal attack surface Collector (ocb build) Only the components you list are compiled in
Grafana backends (Mimir, Loki, Tempo, Pyroscope) Alloy Native writers, profiles
Prometheus scraping at scale, ServiceMonitors already exist Alloy Native scrape, CRD support, built-in target sharding
Secrets or config must change without a restart Alloy local.file / remote.kubernetes.secret re-evaluate live
Central config management for a fleet Alloy (remotecfg) or Collector + OpAMP Supervisor Both manage config, not binaries
Logs from pods without hostPath Alloy loki.source.kubernetes tails through the API
eBPF auto-instrumentation, DB observability Alloy beyla.ebpf, database_observability.*

Name mapping

OTel Collector Alloy
health_check extension /-/healthy, /-/ready on the HTTP server
file_storage extension otelcol.storage.file (public preview in v1.16)
bearertokenauth otelcol.auth.bearer
headers_setter otelcol.auth.headers
pprof extension /debug/pprof on the HTTP server
:8888/metrics (internal telemetry) :12345/metrics
debug exporter otelcol.exporter.debug (experimental)
prometheusremotewrite exporter otelcol.exporter.prometheusprometheus.remote_write

Running both

A two-tier setup (Alloy agents → Collector gateway, or the other way round) is common and fine. Costs to plan for:

  • Two embedded OTel versions. OTTL functions and processor defaults can differ between tiers. A transform that works on one may not parse on the other.
  • Two metric naming conventions for self-monitoring. Alloy exposes otelcol_*_total; older Collectors expose the same counters without _total (lesson 09).
  • Two upgrade cadences. Alloy ships a minor release roughly every three weeks and bumps its embedded Collector and Prometheus with it.

results matching ""

    No results matching ""