OpenTelemetry Protocol (OTLP)
Synopsis
Receives OpenTelemetry logs, metrics, and traces over OTLP/gRPC (default port 4317) and OTLP/HTTP (default port 4318) concurrently. Supports per-signal and per-transport enable/disable toggles, basic and bearer authentication, server TLS and mutual TLS, and configurable worker fan-out via SO_REUSEPORT.
To forward telemetry to an external OTLP-compatible receiver instead, see the OTLP target.
Schema
- id: <numeric>
name: <string>
description: <string>
type: otel
tags: <string[]>
pipelines: <pipeline[]>
status: <boolean>
properties:
address: <string>
otlp_grpc_status: <boolean>
otlp_grpc_port: <numeric>
otlp_http_status: <boolean>
otlp_http_port: <numeric>
otlp_logs_status: <boolean>
otlp_metrics_status: <boolean>
otlp_traces_status: <boolean>
otlp_logs_http_path: <string>
otlp_metrics_http_path: <string>
otlp_traces_http_path: <string>
otlp_max_recv_msg_mib: <numeric>
authentication:
type: <string>
username: <string>
password: <string>
tokens:
- token: <string>
tenant_id: <string>
expire_date: <numeric>
tenants:
- tenant_id: <string>
ip_blocks: <string[]>
tls:
status: <boolean>
cert_name: <string>
key_name: <string>
passphrase: <string>
min_tls_version: <string>
max_tls_version: <string>
client_ca_name: <string>
client_auth_required: <boolean>
insecure_skip_verify: <boolean>
reuse: <boolean>
workers: <numeric>
timezone: <string>
otel_resource_attributes: <string>
Configuration
The following fields are used to define the device:
Device
| Field | Required | Default | Description |
|---|---|---|---|
id | Y | - | Unique numeric identifier |
name | Y | - | Device name |
description | N | - | Optional description |
type | Y | - | Must be otel |
tags | N | - | Optional tags |
pipelines | N | - | Optional pre-processor pipelines |
status | N | true | Enable/disable the device |
address | N | 0.0.0.0 | Listen address applied to both transports |
Transports
Both transports run concurrently and are individually toggleable. At least one transport must be enabled. The gRPC and HTTP ports must be distinct.
gRPC
| Field | Required | Default | Description |
|---|---|---|---|
otlp_grpc_status | N | true | Enable the OTLP/gRPC endpoint |
otlp_grpc_port | N | 4317 | gRPC listen port |
HTTP
| Field | Required | Default | Description |
|---|---|---|---|
otlp_http_status | N | true | Enable the OTLP/HTTP endpoint |
otlp_http_port | N | 4318 | HTTP listen port |
Signals
Logs, metrics, and traces are individually toggleable. At least one signal must be enabled. HTTP paths apply to the OTLP/HTTP transport only.
| Field | Required | Default | Description |
|---|---|---|---|
otlp_logs_status | N | true | Enable the logs signal |
otlp_metrics_status | N | true | Enable the metrics signal |
otlp_traces_status | N | true | Enable the traces signal |
otlp_logs_http_path | N | /v1/logs | HTTP path for the logs signal |
otlp_metrics_http_path | N | /v1/metrics | HTTP path for the metrics signal |
otlp_traces_http_path | N | /v1/traces | HTTP path for the traces signal |
Access List
The access_list property is a source-IP firewall: an ordered list of accept/drop rules over IP blocks, evaluated before the listener parses or authenticates the connection. When it is not set, the firewall is off and adds no overhead.
| Field | Required | Default | Description |
|---|---|---|---|
access_list | N | - | Ordered list of source-IP rules. Omit to disable the firewall. |
access_list[].action | Y | accept or drop (synonyms: allow/permit and deny/reject/block). Unrecognized actions are ignored. | |
access_list[].ip_blocks | Y | One or more IP blocks the rule matches. Comma-separated string or a list. Accepts CIDR (10.0.0.0/24), single IP (172.16.5.10), dotted-netmask (192.168.1.0/255.255.255.0, IPv4 only), and inclusive range (10.5.0.1-10.5.0.50). IPv4 and IPv6. | |
access_list_default | N | auto | Verdict for a source IP that matches no rule: accept or drop. When omitted, it is derived automatically (see below). |
Rules are evaluated top to bottom and the first rule whose blocks contain the source IP decides the outcome. A source IP that matches no rule follows the default policy:
- If any
acceptrule is present, the list is treated as an allowlist and unmatched IPs are dropped. - Otherwise the list is treated as a denylist and unmatched IPs are accepted.
Set access_list_default to override this. An unparseable source IP is judged by the default policy, so an allowlist fails closed.
access_list:
- action: accept
ip_blocks: "10.0.0.0/8, 192.168.0.0/16, 172.16.5.10"
Editing or removing access_list rules applies on the next configuration reconcile with no listener restart. Enabling access_list for the first time on a device that had none takes effect on the next collector restart.
The firewall runs before authentication on both transports. When a source IP is denied, a gRPC request fails with PermissionDenied and an HTTP request receives 403 Forbidden, with no body read.
Authentication
An optional header/metadata authentication layer applied identically to both transports. none disables authentication enforcement.
| Field | Required | Default | Description |
|---|---|---|---|
authentication.type | N | none | Auth scheme: none, basic, or bearer |
authentication.username | Y* | - | Basic-auth username |
authentication.password | Y* | - | Basic-auth password |
tokens | Y** | - | Array of accepted bearer tokens (bare or Bearer-prefixed). Each entry is a plain string or an object (see Multi-Tenancy) |
* = Required when authentication.type is basic.
** = Required when authentication.type is bearer.
Multi-Tenancy
Both transports can attach a tenant_id to each record, resolved from the bearer token first and the source IP as a fallback.
Token-Based
Each entry in the tokens list may be a plain string (matched as-is, with no tenant) or an object that binds the token to a tenant and an optional expiry. Both forms can be mixed in the same list.
| Field | Required | Default | Description |
|---|---|---|---|
tokens[].token | Y | The accepted token secret. Quote long numeric-looking values to preserve them exactly. | |
tokens[].tenant_id | N | - | Tenant identifier attached to records authenticated with this token. Omit for no tenant. |
tokens[].expire_date | N | never | Expiry as epoch seconds (e.g. 1924905600). The token is rejected at and after this instant. Omit for a token that never expires. |
A request presenting an expired or unknown token is rejected with 401 and no record is ingested. When a request authenticates with a token that carries a tenant_id, that tenant is written to _vmetric.event.tenant_id; the client IP always stays at _vmetric.event.request. Use _vmetric.event.tenant_id to route or isolate each customer's data downstream (see Routes).
tokens:
- token: "acme-9f3c...key"
tenant_id: "acme"
expire_date: 1924905600
- token: "globex-7a1b...key"
tenant_id: "globex"
- "legacy-plain-token"
Token changes take effect on the next collector restart.
Requires authentication.type: bearer.
IP-Based
The tenants property maps a source IP to a tenant: an ordered list of rules matched against the connection's (or datagram's) source address before the record enters the pipeline. Multiple IP blocks can map to the same tenant, and multiple tenants are supported. When it is not set, no tenant is attached.
| Field | Required | Default | Description |
|---|---|---|---|
tenants | N | - | Ordered list of source-IP-to-tenant rules. Omit to disable IP-based tenancy. |
tenants[].tenant_id | Y | Tenant identifier attached to records arriving from the matching IP blocks. | |
tenants[].ip_blocks | Y | One or more IP blocks the rule matches. Comma-separated string or a list. Accepts CIDR (10.0.0.0/24), single IP (172.16.5.10), dotted-netmask (192.168.1.0/255.255.255.0, IPv4 only), and inclusive range (10.5.0.1-10.5.0.50). IPv4 and IPv6. |
Rules are evaluated top to bottom and the first rule whose blocks contain the source IP wins. A source IP that matches no rule receives no tenant — the record is still ingested. When a tenant is resolved it is written to _vmetric.event.tenant_id; the client IP always stays at _vmetric.event.request. Use _vmetric.event.tenant_id to route or isolate each customer's data downstream (see Routes).
tenants:
- tenant_id: acme
ip_blocks: "10.0.0.0/24, 10.1.2.3, 192.168.10.0/255.255.255.0"
- tenant_id: globex
ip_blocks: "10.2.0.0/16, 172.16.5.1-172.16.5.50"
Editing or removing tenants rules applies on the next configuration reconcile with no listener restart. Enabling tenants for the first time on a device that had none takes effect on the next collector restart.
When a request presents a valid bearer token whose entry carries a tenant_id, that tenant wins; the tenants IP map is consulted only when the token supplies no tenant.
TLS
TLS applies to both transports.
| Field | Required | Default | Description |
|---|---|---|---|
tls.status | N | false | Enable TLS encryption |
tls.cert_name | Y* | cert.pem | TLS certificate |
tls.key_name | Y* | key.pem | TLS private key |
tls.passphrase | N | - | Passphrase for an encrypted private key |
tls.min_tls_version | N | tls1.2 | Minimum accepted TLS version (tls1.0, tls1.1, tls1.2, tls1.3) |
tls.max_tls_version | N | - | Maximum accepted TLS version. When unset, the highest mutually supported version is negotiated. |
tls.client_ca_name | N | - | CA bundle used to verify client certificates (mTLS) |
tls.client_auth_required | N** | false | Require connecting clients to present a valid certificate. When false, a client certificate is verified only if one is presented. |
tls.insecure_skip_verify | N | false | Skip peer certificate verification. Use only for testing. |
* = Required when tls.status is true.
** = Requires tls.client_ca_name. If tls.client_auth_required is true or tls.client_ca_name is set and the named CA cannot be loaded, the configuration is rejected and the device fails to start.
tls.min_version is a deprecated alias for tls.min_tls_version, honored only when tls.min_tls_version is unset. Use tls.min_tls_version in new configurations.
TLS material fields (cert_name, key_name, ca_name, client_ca_name) accept any of the following:
- File name — resolved relative to the service root directory. Nested paths such as
certs/prod/server.pemare supported. - Absolute path — honored only if it resolves inside the service root. Any path that escapes the root is refused.
- Inline PEM content — used verbatim when the value contains
-----BEGIN. - Environment variable —
${ENV_VAR}. - Vault reference —
$secret{id=...}or$secret{store=...,ref=...}.
Performance and Advanced
| Field | Required | Default | Description |
|---|---|---|---|
otlp_max_recv_msg_mib | N | 4 | Maximum per-RPC gRPC receive size in MiB (floored at 1). Applies to gRPC only. |
reuse | N | true | Enable socket address reuse (SO_REUSEPORT) for worker fan-out |
workers | N | CPU cores | Concurrent processing workers per transport (default: min(NumCPU, 8)) |
timezone | N | - | Per-device timestamp offset adjustment |
otel_resource_attributes | N | - | Comma-separated key=value resource attributes (currently a no-op — see warning below) |
otel_resource_attributes is parsed and validated but is currently a no-op — the raw-bytes ingest path does not apply resource-attribute upserts. Setting it emits a startup warning and does not modify emitted records.
Details
Transports and Ports
The gRPC endpoint (default port 4317) and HTTP endpoint (default port 4318) start concurrently. Each is independently toggleable via otlp_grpc_status and otlp_http_status. Disabling both transports is rejected at config load. The two transports must bind distinct ports — sharing a port between gRPC and HTTP is not supported.
The gRPC listener negotiates h2 (ALPN) and the HTTP listener uses http/1.1. A top-level port or protocol field on the device is forced internally per-transport and has no effect; use otlp_grpc_port and otlp_http_port to set ports.
Signals
Logs, metrics, and traces are individually toggleable. Disabling all three signals is rejected at config load. The per-signal HTTP paths (otlp_logs_http_path, otlp_metrics_http_path, otlp_traces_http_path) apply to the OTLP/HTTP transport; gRPC routes by OTLP service method and the path fields have no effect on it.
Authentication
Three schemes are available: none (open, no credentials required), basic (HTTP Basic Auth using authentication.username and authentication.password), and bearer (token matching against the tokens array). Bearer token matching accepts both bare tokens and Bearer -prefixed values. The authentication layer is applied identically to both transports — gRPC metadata and HTTP headers are checked with the same logic.
TLS and mTLS
Server TLS is enabled via tls.status together with tls.cert_name and tls.key_name. Mutual TLS is configured independently by setting tls.client_ca_name (CA bundle for client certificate verification) and tls.client_auth_required: true. The mTLS layer operates independently of the header/token authentication layer — both can be active simultaneously.
Message Size Limits
otlp_max_recv_msg_mib caps the per-RPC payload size on the gRPC transport (value is converted from MiB to bytes; floored at 1 MiB). The HTTP transport is not bound by this setting.
Performance and Workers
workers sets the per-transport concurrent processing fan-out. The default is min(NumCPU, 8). reuse enables SO_REUSEPORT so multiple workers can accept on the same socket address without lock contention. Both settings apply to each transport independently.
Configuration Reload
Changing any of the following restarts the listener: transport enable flags, port assignments, signal enable flags, HTTP signal paths, TLS material, authentication credentials, worker count, or otlp_max_recv_msg_mib.
The device type for this listener is otel, not otlp. The OTLP target uses type: otlp; this device uses type: otel. Using the wrong type string will cause the device to fail to load.
Examples
The following are commonly used configuration types.
Basic
A minimal OTLP device using default ports and all signals enabled:
Creating an OTLP listener accepting logs, metrics, and traces on default ports... | |
Logs-Only on Custom Ports
Receiving only log signals on non-default gRPC and HTTP ports:
Restricting to logs and moving transports off the default OTLP ports... | |
HTTP-Only with Custom Paths
Disabling gRPC and overriding the default OTLP/HTTP signal paths:
Running only the HTTP transport with application-specific path prefixes... | |
TLS
Enabling server-side TLS on both transports:
Securing the OTLP listener with a server certificate and minimum TLS version... | |
mTLS with Bearer Authentication
Requiring mutual TLS client certificates and bearer token authentication:
Combining mTLS client verification with bearer token enforcement for zero-trust ingestion... | |
Multi-Tenant Ingestion
Serving multiple customers through one OTLP endpoint — collectors that authenticate resolve their tenant by token, while unauthenticated senders fall back to the source-IP map:
Token tenants take precedence; the IP map covers senders that present no tenant-bearing token... | |
A gRPC export from 10.2.0.7 with no tenant token is tagged from the IP map... | |
High-Volume Tuning
Optimizing for large telemetry volumes with increased workers, message size, and pipeline pre-processing:
Scaling worker fan-out and gRPC message limits for high-throughput environments... | |