Configuration reference
LinkMesh has three onboarding paths (managed agent, Standards-Alloy, Standards-otelcol) and each leaves a different set of files on the host. This page lists every location that matters — what owns it, when to touch it, and when to leave it alone. The server’s own storage and database configuration is further down.
Source-of-truth model
Read this once before editing anything.
┌────────────────────────────┐ │ LinkMesh server │ Operator edits ────► │ (UI / API / git-backed │ │ config store) │ └────────────┬────────────────┘ │ remotecfg poll (Alloy) │ OpAMP push (otelcol-contrib) ▼ ┌────────────────────────────┐ │ Collector runtime │ │ (upstream Alloy or │ │ upstream otelcol-contrib) │ └────────────────────────────┘The collector fetches its config directly from the server — over Alloy’s
remotecfg (Connect-RPC) or otelcol’s OpAMP (WebSocket). The LinkMesh agent,
when present, supervises the collector process and reports host context; it is
not in the config path and never writes pipeline files. See
Native remote config for the framing.
Rule of thumb:
| File / surface | Owned by | Survives apt upgrade? | Survives server config push? |
|---|---|---|---|
/etc/default/linkmesh-agent (env, managed path) | Operator | yes (config|noreplace) | yes — server never touches it |
/etc/linkmesh-agent/config.yaml (managed path) | install.sh on enrollment | yes | yes — written once at enrollment |
/etc/linkmesh-agent/{ca,client.crt,client.key} | install.sh on enrollment | yes | no — replaced on cert rotation |
/etc/alloy/config.alloy (bootstrap) | Managed: agent (once); Standards-Alloy: operator | yes | yes — the pipeline arrives via remotecfg, not this file |
/etc/otelcol-contrib/config.yaml (bootstrap) | Operator (Standards-otelcol path) | yes | yes — the pipeline arrives via OpAMP, not this file |
If you edited a pipeline component on the host and your change vanished from the topology, you edited the wrong layer — bootstrap files on disk only carry the connection back to the server. The real pipeline lives in the LinkMesh UI and is delivered to the collector at runtime; it is not persisted to disk by default.
/etc/default/linkmesh-agent — environment file (managed path)
The systemd unit sources this on every (re)start. Variables here are local-to-this-host; the server doesn’t see them.
| Variable | Type | Default | Notes |
|---|---|---|---|
LINKMESH_SERVER | URL | (none) | Set once by one-liner installer. Restart agent after change. |
LINKMESH_TOKEN | string | (none) | Single-use, consumed at first enrollment. Installer clears it after successful enrol. |
LINKMESH_LOG_LEVEL | debug / info / warn / error | info | Raise to debug when investigating connection issues. Live-reloaded — no restart needed. |
LINKMESH_HEARTBEAT_INTERVAL | duration | 15s | How often the agent reports liveness + host context to the server. Raising past 60s makes the fleet table laggy in the UI. Restart required. |
LINKMESH_HOSTNAME_OVERRIDE | string | $(hostname) | Override the hostname the agent reports to the server. Useful for short-lived hosts that should report as a stable logical name. Restart required. |
HTTPS_PROXY / HTTP_PROXY / NO_PROXY | standard | (none) | Honoured for both the server bootstrap call and the gRPC connection. Standard semantics. |
Edit:
sudo nano /etc/default/linkmesh-agentsudo systemctl restart linkmesh-agent# or, for live-reloadable knobs like LOG_LEVEL:sudo systemctl kill -s SIGHUP linkmesh-agent/etc/linkmesh-agent/config.yaml — agent config (managed path)
Written once by the installer when the host enrols. Pointers to certs + the server URL — not a “knobs” file. The server doesn’t push changes here (except cert rotation, which is its own flow).
server: url: your-server.example.com:50051 # gRPC endpoint (mTLS)agent: id: <collector-id-from-server> environment: productioncertificates: caCertPath: /etc/linkmesh-agent/ca.crt clientCertPath: /etc/linkmesh-agent/client.crt clientKeyPath: /etc/linkmesh-agent/client.key insecureSkipVerify: false # default; lab/dev escape hatch — see belowTouch when:
- Migrating the host to a different LinkMesh server (recommended: re-enrol via the one-liner instead).
- Debugging cert paths (rare — installer always uses the same layout).
Do NOT touch the agent.id field. It’s bound to the mTLS cert; mismatch
makes the server reject the connection.
/etc/alloy/config.alloy — Alloy bootstrap
Used by the managed agent path (agent writes it once at linkmesh-agent install alloy time) and the Standards-Alloy path (operator writes it).
Layout and contents are the same either way: a remotecfg block pointing at
the LinkMesh server plus an optional own_metrics push for the topology canvas.
See Onboard Grafana Alloy via remotecfg
for the full template.
Key fields:
| Field | Notes |
|---|---|
remotecfg.url | Server base URL (e.g. https://linkmesh.example.com). Alloy appends the CollectorService path itself — do not add /v1/opamp here (that’s OpAMP, a different protocol). |
remotecfg.id | Stable identifier for this collector. constants.hostname is fine for most fleets. |
remotecfg.bearer_token | Per-collector OTLP token minted in the LinkMesh UI (or via POST /api/v1/collectors/{id}/otlp-token). Bearer scheme only — basic_auth is rejected. |
otelcol.exporter.otlphttp.linkmesh Authorization header | Same token as bearer_token, used by own_metrics push to /v1/metrics. |
The pipeline itself (sources, processors, exporters) does not live here. Alloy fetches it via remotecfg on every poll and merges it into the runtime config in memory.
/etc/otelcol-contrib/config.yaml — otelcol bootstrap (Standards-otelcol path)
Bootstrap config for the upstream OpenTelemetry Collector when onboarding it
agentless via OpAMP. The collector starts with a nop pipeline; LinkMesh
replaces it with the real pipeline over OpAMP within seconds of the first
handshake. See Onboard otelcol-contrib via OpAMP
for the full template.
Key fields:
| Field | Notes |
|---|---|
extensions.opamp.server.ws.endpoint | wss://<server>/v1/opamp — same host/port as the web UI, only the path differs. Use wss:// over any network; ws:// for loopback only. |
extensions.opamp.server.ws.headers.Authorization | Bearer <ENROLLMENT_TOKEN>. Single-use enrollment token minted in the UI. |
extensions.opamp.capabilities | Only reports_effective_config and reports_health. Adding other capability keys makes otelcol-contrib refuse to start with “invalid keys”. |
Like Alloy’s bootstrap, the pipeline is delivered at runtime — the YAML on disk only carries the OpAMP wiring.
systemd units
linkmesh-agent.service (managed path)
[Unit]Description=LinkMesh AgentAfter=network-online.targetWants=network-online.target
[Service]Type=simpleEnvironmentFile=-/etc/default/linkmesh-agentExecStart=/usr/local/bin/linkmesh-agent --config /etc/linkmesh-agent/config.yamlRestart=alwaysRestartSec=5
[Install]WantedBy=multi-user.targetThe agent does not depend on the collector’s unit. It stays up regardless of collector state so it can report health and accept lifecycle commands.
alloy.service (upstream Grafana package)
Installed by linkmesh-agent install alloy (managed path) or by apt install alloy / dnf install alloy directly (Standards-Alloy). The unit ships with
the upstream Grafana package; LinkMesh does not template it. Reads
/etc/alloy/config.alloy.
sudo systemctl status alloysudo journalctl -u alloy -fotelcol-contrib.service (upstream OTel release)
Installed by the upstream .deb / .rpm from the OpenTelemetry release.
Reads /etc/otelcol-contrib/config.yaml.
sudo systemctl status otelcol-contribsudo journalctl -u otelcol-contrib -fFile locations cheat-sheet
| Path | Contents | Notes |
|---|---|---|
/usr/local/bin/linkmesh-agent | Agent binary (managed path) | Installed by the .deb / .rpm |
/etc/default/linkmesh-agent | Env file (operator-owned) | Survives package upgrade |
/etc/linkmesh-agent/config.yaml | Agent connect config | Installer writes once at enrolment |
/etc/linkmesh-agent/ca.crt | LinkMesh internal CA | mTLS trust root |
/etc/linkmesh-agent/client.crt | Agent client cert | Issued at enrolment, rotated on demand |
/etc/linkmesh-agent/client.key | Agent client key | 0600, root-only |
/etc/alloy/config.alloy | Alloy bootstrap (remotecfg + own_metrics) | Pipeline arrives via remotecfg, not from this file |
/etc/otelcol-contrib/config.yaml | otelcol bootstrap (OpAMP) | Pipeline arrives via OpAMP, not from this file |
journalctl -u linkmesh-agent | Agent logs | No flat-file logs by design |
journalctl -u alloy | Alloy logs | Includes remotecfg poll lines |
journalctl -u otelcol-contrib | otelcol logs | Includes OpAMP handshake lines |
LinkMesh server — storage & database
Everything above is the agent / collector host. This section is the server’s
own config, read from /etc/linkmesh/config.yaml on the host running
linkmesh-server (or from environment variables). It controls where the
server keeps its operational state. See
Storage backends for the concepts and
Deploy with MongoDB for setup.
storage
| Key | Type | Default | Notes |
|---|---|---|---|
storage.backend | bolt / mongodb | bolt | Embedded BoltDB database (default, single instance) or external MongoDB (required for high availability). |
storage.boltPath | path | /data/linkmesh/state.db | Where the embedded BoltDB file lives. Ignored when backend is mongodb. |
storage.auditLogRetentionDays | int | 365 | How long audit-log entries survive before the backend prunes them. Applies to both backends; 0 means the 365-day default. |
database — only when storage.backend: mongodb
Set database.uri directly, or set the parts and let the server build the
connection string. uri wins if both are present.
| Key | Type | Default | Notes |
|---|---|---|---|
database.uri | connection string | (none) | Full MongoDB URI. Preferred for mongodb+srv:// (Atlas-style) strings. |
database.server | host or host:port | (none) | Used to build the URI when uri is unset. A bare hostname builds a mongodb+srv:// URI; host:port builds a plain mongodb:// one. |
database.user | string | (none) | Username, folded into the built URI. |
database.password | string | (none) | Password, folded into the built URI. Inject via env rather than committing it. |
database.database | string | signalflow | Database name. The default keeps the legacy name; set linkmesh on a fresh install. |
Embedded default — nothing to set:
storage: backend: boltExternal MongoDB via a single URI:
storage: backend: mongodbdatabase: uri: "mongodb+srv://linkmesh-app:CHANGEME@cluster.example.mongodb.net/linkmesh?retryWrites=true&w=majority"External MongoDB from parts (password injected via env):
storage: backend: mongodbdatabase: server: cluster.example.mongodb.net user: linkmesh-app database: linkmeshEnvironment overrides
Every key takes a LINKMESH_-prefixed environment variable, with dots
flattened to underscores and the name upper-cased — handy for containers
and secret injection. A few database keys also accept shorter aliases:
| Config key | Environment variable |
|---|---|
storage.backend | LINKMESH_STORAGE_BACKEND |
storage.boltPath | LINKMESH_STORAGE_BOLTPATH |
storage.auditLogRetentionDays | LINKMESH_STORAGE_AUDITLOGRETENTIONDAYS |
database.uri | LINKMESH_DATABASE_URI — aliases DATABASE_URI, MONGODB_URI |
database.server | LINKMESH_DATABASE_SERVER — alias MONGODB_SERVER |
database.user | LINKMESH_DATABASE_USER — alias MONGODB_USER |
database.password | LINKMESH_DATABASE_PASSWORD — alias MONGODB_PASSWORD |
When in doubt
The LinkMesh UI is the canonical place to change pipeline / source /
destination / route config. Bootstrap files on disk (config.alloy,
otelcol-contrib config.yaml) only carry the connection back to the server —
edits to pipeline-shaped blocks there will be overridden as soon as remotecfg
or OpAMP delivers the real config on the next poll/push.