Architecture
LinkMesh is a control plane for your OpenTelemetry collectors. You configure pipelines once in the UI; LinkMesh delivers the resulting collector config to every host that should run it, watches each collector’s health, and shows you what each one is actually doing.
Your telemetry never travels through LinkMesh. Collectors send data direct to your observability backends — Loki, Prometheus, Tempo, Grafana Cloud, Kafka, whatever you point them at.
How it all connects
Three things move between the boxes:
- Telemetry (solid arrows) — logs, metrics, and traces flow from your sources through the collectors to your backends.
- Management (dashed line) — the LinkMesh server delivers config to, and receives health, status, and metrics from, every collector it manages.
- Nothing else — no telemetry round-trips through the server, no outbound phone-home from the binaries.
The components
| Component | What it does | Where it runs |
|---|---|---|
| LinkMesh Server | Web UI, REST + gRPC API, OpAMP server, config store. The thing you log into. | One instance per environment (VM or Kubernetes). |
| OpenTelemetry Collector | The runtime that actually receives, processes, and exports telemetry. Runs one of two upstream runtimes — Grafana Alloy (config via remotecfg) or otelcol-contrib supervised by opampsupervisor (config via OpAMP). | One per managed host, or per Kubernetes pod / DaemonSet. |
| LinkMesh Agent (optional) | An edge connector for onboarding — it discovers the services and log sources on a host (pods, namespaces, and workloads on Kubernetes) and feeds them into the onboarding wizard so you configure the collector from what’s really there. It produces no telemetry, is not in the config path, and is not a collector. | Alongside a collector, only when you want discovery-assisted onboarding. |
The collector is the unit that always exists on a managed target. The agent is a separate, optional helper — see The agent.
The two collector runtimes
A collector runs one of two runtimes. Both are upstream binaries — LinkMesh ships no collector distribution of its own. Once configured a collector behaves the same either way; only how it receives its config differs.
Grafana Alloy — config via remotecfg (pull)
Alloy polls the LinkMesh server for its pipeline config over Bearer-authenticated HTTPS and pushes its own internal metrics back as OTLP. Use this runtime when you want a single, self-contained collector binary that fetches its own config.
otelcol-contrib + OpAMP — config via push
An upstream otelcol-contrib supervised by upstream opampsupervisor
connects to the LinkMesh server over OpAMP (WebSocket Secure); the
server pushes remote config and the supervisor applies it. Use this
runtime when you’re standardising on the OpenTelemetry OpAMP ecosystem.
Pick per collector based on which ecosystem you’re standardising on — both are first-class, and a single LinkMesh server manages a mix of them.
The optional onboarding agent
Neither runtime needs the LinkMesh agent. The agent is a separate, optional edge connector: run it alongside a collector when you want LinkMesh to discover what’s worth collecting — running services and log sources on a VM, or pods, namespaces, and workloads on Kubernetes — and surface that into the onboarding wizard. It never carries telemetry and never delivers collector config. Full detail on the agent page.
How data flows through a pipeline
Inside a single pipeline, telemetry flows through a fixed sequence of stages. The meters on either end of the chain are what the UI shows you for “in” and “out” volume per pipeline.
flowchart LR
S[Source] --> IM[Input meter]
IM --> SF[Stream filters]
SF --> P[Processors]
P --> OM[Output meter]
OM --> D[Destination]
classDef io fill:#eef4ff,stroke:#1E63FF,stroke-width:2px,color:#0b1b3a;
classDef stage fill:#ffffff,stroke:#94a3b8,stroke-width:1.5px,color:#0b1b3a;
classDef meter fill:#f8fafc,stroke:#06B6D4,stroke-width:1.5px,color:#0b1b3a;
class S,D io;
class SF,P stage;
class IM,OM meter;
- Stream filters decide whether a record continues — drop noisy logs, sample traces, rate-limit per service.
- Processors modify records that pass — mask PII, add labels, batch.
- Meters are read-only — they count what entered and what left, so the UI can show you the throughput delta a pipeline produced.
See Pipeline for the full reference, or Build your first pipeline for a walkthrough.
How config reaches your hosts
When you save a change in the UI, this is what happens on the wire. The first three steps are identical for both runtimes; the last few differ because each runtime fetches/receives config its own way.
sequenceDiagram
autonumber
actor You as Operator
participant UI as LinkMesh UI
participant Srv as LinkMesh Server
participant Repo as Config (Git-backed)
participant Alloy as Alloy (remotecfg)
participant Supervisor as opampsupervisor
participant Col as otelcol-contrib
You->>UI: Save pipeline change
UI->>Srv: POST /api/v1/pipelines/...
Srv->>Repo: Commit new YAML
rect rgba(30, 99, 255, 0.06)
Note over Alloy,Srv: Alloy runtime — collector PULLS via remotecfg
Alloy->>Srv: GET /api/v1/remotecfg (poll, Bearer auth)
Srv-->>Alloy: New River config + hash
Alloy-->>Srv: Status + own_metrics (OTLP /v1/metrics)
end
rect rgba(6, 182, 212, 0.06)
Note over Supervisor,Col: otelcol + OpAMP runtime — server PUSHES via OpAMP
Srv->>Supervisor: OpAMP RemoteConfig (over WSS)
Supervisor->>Col: Restart with effective.yaml
Col-->>Supervisor: Health + effective config
Supervisor-->>Srv: EffectiveConfig + status
end
Srv-->>UI: Live status update
Three things worth knowing:
- A save is durable as soon as step 3 succeeds. If a collector is offline, it picks up the new config when it reconnects — Alloy on its next poll, OpAMP on its next session.
- The collector reports back what config it actually loaded (steps 6 / 11). The UI compares that to what was committed; mismatches show up as a “drift” badge on the collector.
- The optional onboarding agent doesn’t appear here at all — it’s not
in the config path. Config flows directly between the collector and
the server (Alloy via
remotecfg, otelcol via OpAMP). When present, the agent only helps you discover what to collect during onboarding; it never sees config bytes.
Where your data lives
Three different stores, each with a different lifetime and purpose:
flowchart TB
subgraph Srv["LinkMesh Server"]
Cfg[(Config repository<br/>Git-backed YAML)]
Run[(Runtime state<br/>collector status, metrics, events)]
end
subgraph Host["Each managed host"]
Local[(Collector config<br/>on local disk)]
end
Cfg -- "delivered on change" --> Local
Local -- "status + health" --> Run
- Config repository — the source of truth for what every collector should be running. Versioned, auditable, restorable. Survives any server restart.
- Runtime state — live operational data: which collectors are connected, last-seen timestamps, recent throughput, recent errors. Rebuilds itself from collector heartbeats if lost. This lives in the server’s storage backend — an embedded BoltDB database by default, or an external MongoDB database for high-availability deployments.
- Local collector config — a cached copy of what the server most recently delivered. The collector reads from here on startup, which is why collectors keep running across LinkMesh server restarts.
Your telemetry data itself is never stored by LinkMesh. It moves through the collector and out to your backends; LinkMesh only sees the counters.
Data-handling guarantees
LinkMesh is on-premises by design: telemetry stays inside customer infrastructure, and product binaries don’t phone home. The architectural implications for a security review live on the marketing site — Trust on linkmesh.io lays out the data-handling story with verifiable, plain-language claims.