Configure Mermin with HCL (HashiCorp Configuration Language) or YAML. This page describes the config file format, precedence, and structure. Section-specific options are documented in the linked pages.
Mermin accepts HCL (recommended) or YAML. Supported file extensions: .hcl, .yaml, .yml. Use an .hcl file for clear syntax and good error messages.
To use YAML, convert from HCL with the fmtconvert tool (go install github.com/genelet/determined/cmd/fmtconvert@latest) and pass the result to --config:
fmtconvert -from hcl -to yaml config.hcl > config.yaml
mermin --config config.yamlConfiguration is merged in this order (later overrides earlier):
- Built-in defaults
- Config file (path from
--configorMERMIN_CONFIG_PATH) - Environment variables (global options only)
- Command-line flags (global options only)
These global options can be set via environment variables or CLI:
| Option | CLI flag | Env var |
|---|---|---|
| Config path | --config |
MERMIN_CONFIG_PATH |
| Log level | --log-level |
MERMIN_LOG_LEVEL |
| Auto reload | --auto-reload |
MERMIN_CONFIG_AUTO_RELOAD |
| Worker threads | --worker-threads |
MERMIN_WORKER_THREADS |
Options like shutdown_timeout and everything under pipeline, export, etc. are config-file only.
By default Mermin automatically detects how many CPU cores are available to the process. In Kubernetes this respects the pod's CPU limit, so the thread pool stays within the container's budget and avoids throttling. On bare metal it uses the number of logical CPUs. No configuration is needed for this to work correctly.
Use --worker-threads (or MERMIN_WORKER_THREADS) only when you need to fix the count
explicitly, for example when cores are pinned or you want to limit concurrency:
# Fixed count — bypasses auto-detection
mermin --worker-threads 4
MERMIN_WORKER_THREADS=4 merminLeave the flag unset in Kubernetes; the runtime will automatically stay within the pod's CPU limit.
Example: with log_level = "info" in the file, export MERMIN_LOG_LEVEL=debug or mermin --log-level=debug --config=config.hcl yields log_level = debug.
A config file is optional. Omit --config and MERMIN_CONFIG_PATH to use built-in defaults. To use a file:
- CLI:
mermin --config /path/to/config.hcl - Env:
MERMIN_CONFIG_PATH=/path/to/config.hcl - Kubernetes: Create a ConfigMap from the file, mount it in the pod, and pass the path to
mermin --config.
The file must exist and have a supported extension. Subcommands (e.g. mermin diagnose bpf) do not load the main config. Use mermin --help or mermin diagnose --help for usage.
When auto_reload = true (or --auto-reload / MERMIN_CONFIG_AUTO_RELOAD=true), Mermin watches the config file and reloads on change without restart.
Flow capture may pause briefly during reload. Some changes (e.g. interface selection or RBAC) still require a full restart.
Without a config file, Mermin uses built-in defaults and does not configure an exporter — flow data is not sent anywhere. To send flow traces to an OTLP endpoint with default settings, create a config file that sets only the export block:
export "traces" {
otlp = {
endpoint = "http://otel-collector:4317"
protocol = "grpc"
}
}Omit other blocks (discovery, pipeline, api, etc.) to use built-in defaults. Run with mermin --config config.hcl. For more complete examples, see Configuration Examples.
Top-level settings. See Global Options.
log_level = "info"
log_color = false
auto_reload = false
shutdown_timeout = "28s"
pipeline {
flow_capture {
flow_stats_capacity = 100000
flow_events_capacity = 1024
}
flow_producer {
workers = 4
worker_queue_capacity = 2048
flow_store_poll_interval = "5s"
flow_span_queue_capacity = 16384
}
k8s_decorator {
decorated_span_queue_capacity = 32768
}
}Health HTTP server and internal Prometheus metrics. See Internal Server and Metrics.
internal "server" {
enabled = true
listen_address = "0.0.0.0"
port = 8080
}
internal "metrics" {
enabled = true
listen_address = "0.0.0.0"
port = 10250
debug_metrics_enabled = false
stale_metric_ttl = "5m"
# histogram_buckets { ... } # optional overrides
}Setting internal.metrics.debug_metrics_enabled = true enables high-cardinality metrics and can increase memory use; enable only for debugging.
eBPF packet parsing. See Network Packet Parser.
parser {
geneve_port = 6081
vxlan_port = 4789
wireguard_port = 51820
}Interfaces and Kubernetes discovery. See Network Interface Discovery and Kubernetes Informers.
If you omit interfaces, built-in defaults target CNI interfaces (e.g. veth*, tunl*, vxlan*, cali*, cilium_*). The example below overrides with physical interfaces:
discovery "instrument" {
interfaces = ["eth*", "ens*"] # override; defaults are CNI-oriented
auto_discover_interfaces = true
tc_priority = 1
tcx_order = "first" # or "last"
}
discovery "informer" "k8s" {
kubeconfig_path = ""
informers_sync_timeout = "30s"
selectors = [{ kind = "Pod" }, { kind = "Service" }]
# owner_relations { ... }
# selector_relations = [ ... ]
}Owner and selector relations for flow enrichment. See Owner Relations and Selector Relations.
Which Kubernetes metadata to extract and how to associate it with flows. See Flow Attributes. If you omit the attributes block, default Kubernetes attribution is applied.
An empty attributes {} block disables attribution.
Filter flows by address, port, transport, type, interface, and other dimensions. See Flow Filtering. Each filter block has a label (e.g. "source"); inside it you can set match and not_match for:
address,port,transport,typeinterface_name,interface_index,interface_macconnection_stateip_dscp_name,ip_ecn_name,ip_ttl,ip_flow_labelicmp_type_name,icmp_code_nametcp_flags_tags
Example:
filter "source" {
address = { match = ["10.0.0.0/8"] }
port = { match = ["80", "443"] }
transport = { match = ["tcp"] }
}Flow span generation, timeouts, Community ID, trace correlation, and hostname resolution. See Flow Span Options. All options are config-file only.
span {
max_record_interval = "60s"
generic_timeout = "30s"
icmp_timeout = "10s"
tcp_timeout = "20s"
tcp_fin_timeout = "5s"
tcp_rst_timeout = "5s"
udp_timeout = "60s"
community_id_seed = 0
trace_id_timeout = "24h"
}Trace export to OTLP and/or stdout. See OTLP Exporter and Console Exporter.
export "traces" {
stdout = "text_indent"
otlp = {
endpoint = "http://otel-collector:4317"
protocol = "grpc"
timeout = "10s"
max_batch_size = 512
max_batch_interval = "5s"
max_queue_size = 2048
max_concurrent_exports = 1
max_export_timeout = "30s"
headers = { "x-custom" = "value" }
auth = {
basic = { user = "username", pass = "password" }
}
tls = {
insecure_skip_verify = false
ca_cert = "/etc/certs/ca.crt"
client_cert = "/etc/certs/client.crt"
client_key = "/etc/certs/client.key"
}
}
}Mermin's own telemetry. See Internal Tracing.
internal "traces" {
span_fmt = "full"
stdout = { format = "text_indent" }
otlp = { endpoint = "http://otel-collector:4317", protocol = "grpc" }
}Configuration is validated on startup. Invalid config (unknown field, invalid value, missing file, or unsupported extension) causes Mermin to exit with a non-zero exit code and print the error to stderr. Fix the file and restart (or rely on auto-reload after fixing). In Kubernetes, Mermin logs a memory warning if estimated pipeline usage exceeds 80% of the container limit; see Pipeline and Troubleshooting.
HCL config files (not YAML) support the env function to read environment variables — useful for secrets or environment-specific values without hardcoding. The function evaluates when the config loads and again on reload.
-
env("VAR_NAME")Returns the value of the environment variable, or an empty string if unset. Mermin logs a warning when the variable is not set. -
env("VAR_NAME", "default")Returns the variable value if set, otherwise the second argument. Mermin logs a warning when the variable is not set and the default is used.
You can use env anywhere a string is accepted (e.g. log_level, api.listen_address, export "traces" { otlp = { endpoint = ... } }, auth.basic.pass).
You can use it in lists (e.g. discovery "instrument" { interfaces = [env("IFACE")] }) and in string interpolation (e.g. "prefix-${env("VAR")}-suffix"). Examples that match the behavior tested in the codebase:
# Top-level with default
log_level = env("MERMIN_LOG_LEVEL", "info")
# OTLP endpoint and auth (strings)
export "traces" {
otlp = {
endpoint = env("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317")
auth = {
basic = {
user = "mermin"
pass = env("OTLP_PASSWORD")
}
}
}
}
# HTTP server listen address with interpolation
internal "server" {
listen_address = "prefix-${env("SERVER_HOST")}-suffix"
port = 8080
}YAML configs do not support env; use HCL if you need it, or inject values before conversion.
- Configuration Examples: full example configs (production, development, CNI, high-throughput, security).
- Section reference:
| Section | Description |
|---|---|
| Global Options | Configure Global Agent Options |
| Internal Server | Configure Internal Server |
| Internal Prometheus Metrics | Configure Internal Prometheus Metrics |
| Network Packet Parser | Configure Parsing of Network Packets |
| Network Interface Discovery | Configure Discovery of Network Interfaces |
| Kubernetes Informer Discovery | Configure Discovery of Kubernetes Informer |
| Kubernetes Owner Relations | Configure Owner Relations of Kubernetes Resources |
| Kubernetes Selector Relations | Configure Selector Relations of Kubernetes Resources |
| Flow Span Kubernetes Attribution | Configure Kubernetes Attribution of Flow Spans |
| Flow Span Filters | Configure Filtering of Flow Spans |
| Flow Span Producer | Configure Producing of Flow Spans |
| OpenTelemetry OTLP Exporter | Configure OpenTelemetry OTLP Exporter |
| OpenTelemetry Console Exporter | Configure OpenTelemetry Console Exporter |
| Internal Tracing | Configure Internal Tracing Exporter |
| Flow Processing Pipeline | Configure Flow Processing Pipeline |
- Start minimal; add options as needed.
- Comment non-obvious choices.
- Keep config in version control.
- Test changes outside production.
- Use metrics to confirm behavior.
- Prefer auto-reload for iterative tuning.
- Keep secrets in env vars or Kubernetes secrets, not in the config file.
{% tabs %} {% tab title="Essential Configuration" %}
- Configure Network Interfaces: Select which interfaces to monitor
- Set Up OTLP Export: Send flows to your backend with TLS
- Configure Global Options: Logging, timeouts, and CLI flags {% endtab %}
{% tab title="Advanced Configuration" %}
- Filter Flows Before Export: Reduce noise and storage costs
- Tune the Processing Pipeline: Optimize for high-throughput environments
- Review Complete Examples: Full production configurations {% endtab %} {% endtabs %}
- Troubleshoot Configuration Issues: Resolve HCL syntax and validation errors
- GitHub Discussions: Ask questions and share configurations