Send metrics & infrastructure to Epok
Updated Jul 28, 2026 · 1d ago
Epok accepts metrics over two open protocols: OTLP and Prometheus remote_write. Anything that speaks either — the OpenTelemetry collector, node_exporter, kube-state-metrics, cAdvisor, Telegraf, or a database exporter — works with no proprietary agent.
The one thing that matters for correlation: give each metric a service label (or an OTLP service.name resource attribute). Epok normalizes the common aliases automatically — node_exporter instance becomes host, KSM pod/namespace carry through — so the metric lands on the right service in the investigation instead of a separate dashboard.
Endpoints: /v1/metrics (OTLP) · /api/v1/write (remote_write) · API key: app.getepok.dev → Settings → API Keys
OpenTelemetry hostmetrics (recommended)
The hostmetrics receiver already emits system.cpu/memory/filesystem.utilization as 0–1 fractions — exactly the shape Epok's saturation detection wants. Add a service.name so the host joins the service it runs.
receivers:
hostmetrics:
collection_interval: 30s
scrapers: { cpu: {}, memory: {}, filesystem: {}, load: {}, network: {} }
processors:
resource:
attributes:
- key: service.name # joins these host metrics to your service
value: checkout-service
action: upsert
exporters:
otlphttp/epok:
metrics_endpoint: https://ingest.getepok.dev/v1/metrics
encoding: json # set this — accepted by every build (see note below)
headers: { Authorization: "Bearer ${EPOK_API_KEY}" }
service:
pipelines:
metrics:
receivers: [hostmetrics]
processors: [resource]
exporters: [otlphttp/epok]On encoding: json. Set it. OTLP/JSON is accepted by every build of the metrics endpoint, so this line is the one setting that is correct everywhere. A stock collector defaults to protobuf instead — newer builds also accept that, but older ones reject it, and the rejection is quiet in a way that costs you an afternoon: traces and logs keep flowing while metrics alone are dropped, so it reads as a missing-metrics problem rather than an encoding one. Keeping encoding: json in your config removes the question.
Prometheus node_exporter
node_exporter emits raw bytes and seconds, not utilization — so add two recording rules that derive the system_*_utilization series Epok's saturation detection and the investigation infra panel read. Attach a service label at scrape time.
scrape_configs:
- job_name: node
static_configs:
- targets: ["node-exporter:9100"]
labels:
service: checkout-service # joins this service in Epok
rule_files: [epok_recording.yml]
remote_write:
- url: https://ingest.getepok.dev/api/v1/write
authorization:
type: Bearer
credentials: YOUR_API_KEY # paste your key literally — Prometheus does not expand env varsgroups:
- name: epok-derived-utilization
rules:
- record: system_memory_utilization
expr: 1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)
- record: system_filesystem_utilization
expr: 1 - (node_filesystem_avail_bytes / node_filesystem_size_bytes)
- record: system_cpu_utilization
expr: 1 - avg by (instance, service) (rate(node_cpu_seconds_total{mode="idle"}[5m]))Kubernetes (kube-state-metrics)
Scrape kube-state-metrics and remote_write it to Epok. This is what powers the pod-Pending, restart, and node-pressure signals that pair with Epok's log-based Kubernetes detection — pod and namespace labels carry through automatically.
scrape_configs:
- job_name: kube-state-metrics
static_configs:
- targets: ["kube-state-metrics.kube-system.svc:8080"]
remote_write:
- url: https://ingest.getepok.dev/api/v1/write
authorization:
type: Bearer
credentials: YOUR_API_KEY # paste your key literally — Prometheus does not expand env varsDatabase & middleware exporters
postgres_exporter, kafka_exporter, redis_exporter, and friends correlate directly with the service that depends on them — replication lag, consumer lag, and pool saturation become evidence in the incident. Scrape them with a service label and remote_write, exactly like node_exporter above.
Confirm it's working
- Wait ~1–2 minutes, then open Metrics in Epok (app.getepok.dev → Metrics) — your host series (
system_cpu_utilization, memory, filesystem) appear in the metric list. - Open the Service page for the
serviceyou set — the infra strip shows live CPU and memory for that service. - The metric detectors (saturation, anomaly, slow-drift, reporting-gap) arm automatically for those series — no rules to write. They hold fire for roughly the first 3 hours after your first metric arrives: a baseline built from minutes of startup noise generates false pages, so the gate is deliberate.
Nothing showing up? Check your collector / Prometheus logs for a non-2xx on the ingest call. 400means the body did not parse — the payload does not match the content type it was sent under (a truncated batch, a bad gzip, or an encoding the build in front of you does not decode); 403means a key arrived and was rejected — wrong, rotated, or missing the ingest scope, and the usual cause is Prometheus shipping ${EPOK_API_KEY} literally, since it does not expand env vars; 401 means no credential was found at all, so the auth block itself is missing or malformed; 429 means you are over your daily volume or rate limit; 503means metrics ingest isn't switched on for your account — a gate, not a fault, and nothing you can fix from the config.
Your first look
Wait one to two minutes after the first write, then open Infrastructure → Metrics. It opens filtered to infrastructure-origin series. Type system_into the metric filter box: if you followed the hostmetrics or recording-rule sections above, the three utilization series are there — system_cpu_utilization, system_memory_utilization, system_filesystem_utilization.
The one thing worth doing first: select system_filesystem_utilization and use Break down by to split it by host. Every label you attached is a chip. A fleet of flat lines with one climbing away from the rest is exactly the shape capacity forecasting is built to catch — and seeing it split cleanly is your proof that the service and host labels landed, which is what decides whether these metrics show up inside an investigation or sit in a corner as their own dashboard.
If the breakdown chips are missing or everything collapses into one line, the labels did not make it — go back to the service label on the scrape config or the resource processor before going further. Then open Infrastructure → Host Map for the same data as one row per host.
Next
- Instrument your app for tracing — metrics say a host is saturated; spans say which request path pays for it. Correlating the two is where a root-cause verdict comes from.
- Browser RUM & session replay — the last hop, from a saturated node to the user who felt it.
- What watches these series — every detector, what it fires on, and what a series needs to be eligible for it.
- Limits — payload caps, daily volume, rate limits, retention.