Send your first telemetry
Fanout accepts traces, logs and metrics over OTLP/gRPC and OTLP/HTTP on the
same port as the application: 7520 by default. Both require the ingest token.
The ingest token
Section titled “The ingest token”The raw ingest token is shown once, immediately after you create the first
administrator. It is fo_-prefixed, and Fanout stores only its hash.
Send it as a bearer token; this is the only accepted ingest credential form:
Authorization: Bearer fo_<token>Two things are worth knowing before you debug a rejection:
- Browser sessions and MCP tokens are not ingest credentials. They are different credentials for different surfaces, and an exporter presenting one is rejected like any other unauthenticated caller.
- Before setup completes, ingest rejects everything. With no administrator there is no token to check against, so a collector started ahead of setup fails until you have finished it. That is the intended order: the instance does not accept telemetry it cannot attribute.
Configure an SDK
Section titled “Configure an SDK”export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:7520export OTEL_EXPORTER_OTLP_PROTOCOL=grpcexport OTEL_EXPORTER_OTLP_HEADERS="authorization=Bearer%20fo_<token>"export OTEL_SERVICE_NAME=checkoutexport OTEL_RESOURCE_ATTRIBUTES=service.namespace=storefrontFor OTLP/HTTP, keep the same endpoint and set
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf. Use https:// when Fanout or a
proxy terminates TLS. A general OTLP endpoint is a base URL; signal-specific
variables such as OTEL_EXPORTER_OTLP_TRACES_ENDPOINT must include their path
(/v1/traces, /v1/metrics or /v1/logs).
Configure a Collector
Section titled “Configure a Collector”receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318
exporters: otlp/fanout: endpoint: "https://fanout.example.com:443" headers: Authorization: "Bearer ${env:INGEST_TOKEN}"
service: pipelines: traces: receivers: [otlp] exporters: [otlp/fanout] logs: receivers: [otlp] exporters: [otlp/fanout] metrics: receivers: [otlp] exporters: [otlp/fanout]The scheme and the port both earn their place. https:// puts the exporter on
TLS and keeps it there, whatever tls.insecure says below it. The port is not
optional: the gRPC exporter validates its endpoint before dialling and refuses
to start without one, with address fanout.example.com: missing port in address. gRPC’s own default of 443 belongs to a resolver path this exporter
never reaches.
If nothing in front of Fanout terminates TLS — a local instance, or one on a
private network — the endpoint is http://fanout.example.com:7520 and the
exporter needs tls: { insecure: true } under otlp/fanout, or it will fail
to connect. Connect telemetry in the application generates this block with
your own endpoint already in it, and includes that line when it applies.
A Collector is optional. Fanout speaks the same protocol your SDKs already export, so it can sit directly behind them — but if you already run a Collector for sampling, enrichment or fan-out to a second backend, Fanout is just another exporter in the pipeline.
Watch hosts, containers and endpoints
Section titled “Watch hosts, containers and endpoints”The default System overview dashboard combines application traces with
Collector metrics. Use the Collector contrib distribution for hostmetrics,
docker_stats and httpcheck. Run one Collector on each observed host, with
access to the host filesystem and Docker socket:
receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 hostmetrics: collection_interval: 30s scrapers: cpu: {} memory: {} filesystem: {} docker_stats: endpoint: unix:///var/run/docker.sock collection_interval: 30s httpcheck: collection_interval: 30s targets: - endpoint: https://app.example.com/health method: GET
processors: resourcedetection: detectors: [system] system: hostname_sources: [os] resource: attributes: - key: service.namespace value: storefront action: upsert batch: {}
exporters: otlp/fanout: endpoint: https://fanout.example.com:443 headers: Authorization: "Bearer ${env:INGEST_TOKEN}"
service: pipelines: traces: receivers: [otlp] processors: [batch] exporters: [otlp/fanout] logs: receivers: [otlp] processors: [batch] exporters: [otlp/fanout] metrics: receivers: [otlp, hostmetrics, docker_stats, httpcheck] processors: [resourcedetection, resource, batch] exporters: [otlp/fanout]Set INGEST_TOKEN in the Collector environment. Use your Fanout address and
namespace, and the plaintext exporter settings above when TLS is absent.
If the Collector runs in a container, mount the host filesystem read-only and
set hostmetrics.root_path to that mount; give the Collector the observed
host’s host.name, rather than its own container hostname. Mount the Docker
socket only where container metrics are needed.
Run httpcheck from a second machine outside the observed stack. On that
Collector, reuse the exporter and resource processors, but use a metrics
pipeline with receivers: [httpcheck]. Its host.name identifies where the
check ran. A check on the application host cannot see every DNS, CDN or proxy
failure that outside visitors encounter; the dashboard warns when all checks
run on hosts that also run observed services.
- Is it reachable? uses
httpcheck.statusandhttpcheck.duration, with the target’shttp.urland the reporting Collector’shost.name. - Is anything failing or slow? uses application spans. Client spans with
peer.serviceorserver.addressidentify uninstrumented dependencies; error status makes their failures visible. - Is anything running out? uses host CPU counters, memory and filesystem usage by state and mountpoint, plus container CPU and memory usage and limits. Capacity warns at 80% and is bad at 90%.
- What changed? uses
service.versionon application resources, along with runtime and SDK attributes. Send a new version when deploying a service.
Silence is not health. After two reporting intervals, an endpoint with no result shows no recent result, and a host or container shows not reporting, both in the bad state. A receiver that has never sent data shows an empty panel. If the machine running Fanout fails, Fanout cannot report that failure on its own page: monitor Fanout itself from outside that machine.
Existing users keep their saved copy of the default dashboard. New system sections appear in newly created default dashboards; existing copies are not automatically replaced.
Namespaces
Section titled “Namespaces”Fanout reads service.namespace from each OTLP resource, falling back to
FANOUT_DEFAULT_NAMESPACE (default) when it is absent. Namespaces let one
instance hold several services’ telemetry without them running together.
A query with no namespace set now spans all of them. Scoping is opt-in: you narrow to one namespace deliberately, rather than being silently confined to one and wondering where the rest of your telemetry went.
They are not an authorisation boundary. A namespace is not a user, an organisation or a tenant, and anyone who can query the instance can query every namespace in it. An instance is the trust domain.
One listener
Section titled “One listener”FANOUT_ADDR controls the shared listener. The native default is
127.0.0.1:7520; the image sets :7520. Set FANOUT_ADDR=:7520 for remote
clients, or publish 7520:7520 in Docker. There are no separate ingest address
settings.
When using a reverse proxy, enable HTTP/2 to the backend for gRPC. Plaintext Fanout accepts prior-knowledge HTTP/2 (h2c); a TLS listener negotiates HTTP/2 through ALPN. HTTP/1.1 forwarding works for OTLP/HTTP, but cannot carry gRPC. See terminate TLS.