DEV Community

Cover image for The OpenTelemetry K8s Cost Trap (And How to Fix It)
Jakson Tate
Jakson Tate

Posted on Originally published at servermo.com

The OpenTelemetry K8s Cost Trap (And How to Fix It)

You instrumented your applications correctly, adopted OpenTelemetry (OTel), and finally achieved end-to-end tracing across your microservices. The engineering team is thrilled. Then, Finance forwards you the monthly cloud invoice, and the CTO demands a meeting. Your observability costs have surged 10x in a single month.

What happened? The truth about OpenTelemetry in Kubernetes is that while the OTel software is open-source and free, the infrastructure required to transport and store the telemetry is not. When you combine the explosive data volume of OTel auto-instrumentation with the rapid churn of Kubernetes autoscaling, you create a perfect financial storm.

Here is an SRE/FinOps breakdown exposing hidden cloud network taxes, metric cardinality explosions, distributed trace sampling bugs, and how to fix them.


Phase 1: HPA and The Cardinality Explosion

When investigating why observability SaaS bills (like Datadog or Splunk) explode, Kubernetes clusters often reveal a hidden culprit: the Horizontal Pod Autoscaler (HPA). As traffic spikes, your HPA rapidly spins up dozens of new pods, and then destroys them when traffic subsides.

Vendors charge heavily for Custom Metrics based on "Cardinality" (the number of unique metric combinations). Every time HPA creates a new pod, it generates ephemeral labels like k8s.pod.uid or dynamic k8s.pod.name.

🚨 The OTTL Context Blindspot: These labels are Resource Attributes, not Datapoint Attributes. If you try to strip them using context: datapoint in your OpenTelemetry Transformation Language (OTTL) configuration, it will silently fail.

The SRE Fix

Filter high-cardinality attributes at the OTel Collector using the resource context before exporting metrics:

processors:
  transform/metrics:
    error_mode: ignore
    metric_statements:
      - context: resource # CRITICAL: You must use 'resource' context for K8s pod labels!
        statements:
          # Strip ephemeral pod identifiers before export to prevent billing spikes
          - delete_key(attributes, "k8s.pod.uid")
          - delete_key(attributes, "k8s.pod.name")
Enter fullscreen mode Exit fullscreen mode

Phase 2: The Stealth Egress Tax (NAT & Cross-AZ)

The most devious OpenTelemetry Kubernetes cost trap doesn't come from your observability vendor. It comes directly from AWS, Azure, or GCP. Cloud providers charge heavily for data leaving their network or moving between boundaries:

  • The Cross-AZ Penalty: A best-practice OTel architecture uses Edge DaemonSets that forward data to a centralized Gateway Collector. If a DaemonSet in us-east-1a sends 5TB of traces to a Gateway in us-east-1b, cloud providers charge ~$0.01/GB in both directions.
  • The NAT Gateway Processing Fee: If your Kubernetes cluster sits in a private subnet, sending telemetry to a public SaaS backend requires passing through a NAT Gateway. You pay $0.045/GB for NAT processing PLUS $0.09/GB for Internet Egress.

The Reality: You are paying ~$0.135 per GB just to move your own data, before your vendor even bills you for ingestion!


Phase 3: Fixing the Sampling Blindspot

To survive egress taxes, you must aggressively reduce trace volume before it leaves your cluster. However, defaulting to Head-Based Sampling randomly drops traces at inception, destroying 90% of your critical P99 ERROR traces. You must use Tail-Based Sampling.

🚨 The Multi-Replica Topology Error: Tail sampling requires the processor to evaluate the complete trace. If you run multiple OTel Gateway Pods, Span 1 might hit Gateway A, and Span 2 might hit Gateway B. The tail sampling logic gets confused and drops critical error traces.

The SRE Cure

Deploy a Load Balancing Exporter on your Edge Agents (DaemonSets) with routing_key: "traceID". This ensures all spans of the same trace reliably hit the exact same Gateway replica where the tail-sampling processor lives.

# 1. Edge Agent (DaemonSet) Configuration
exporters:
  loadbalancing:
    routing_key: "traceID" # Ensure all spans for a trace reach the same Gateway replica
    protocol:
      otlp:
        endpoint: [http://gateway-service.observability.svc.cluster.local:4317](http://gateway-service.observability.svc.cluster.local:4317)

# ----------------------------------------------------

# 2. Gateway Collector Configuration
processors:
  tail_sampling:
    decision_wait: 10s # Buffer time to wait for trace completion
    num_traces: 100000 # Memory sizing (Monitor OOM kills!)
    policies:
      # Policy 1: Always keep 100% of Errors
      - name: keep-errors
        type: status_code
        status_code:
          status_codes: [ERROR]
      # Policy 2: Sample only 5% of healthy normal traffic
      - name: sample-healthy
        type: probabilistic
        probabilistic:
          sampling_percentage: 5
Enter fullscreen mode Exit fullscreen mode

Phase 4: Repatriating Observability to Bare Metal

Even with aggressive Tail-Based sampling, high-throughput microservices will still generate terabytes of vital telemetry data. The public cloud billing model fundamentally penalizes you for deeply monitoring your own infrastructure.

This is why Elite engineering organizations are repatriating heavy observability stacks (Prometheus, Grafana Loki, ClickHouse) to ServerMO Dedicated Bare Metal Servers:

  1. Zero Egress Fees: ServerMO provides Unmetered or massive Flat-Rate Bandwidth. Stream 50TB+ of OpenTelemetry data daily with zero cross-AZ or NAT fees.
  2. Free Write IOPS: Observability is a 100% write-heavy workload. Cloud providers charge astronomical Provisioned IOPS (e.g., AWS io2) fees for heavy storage writes. ServerMO's direct-attached Enterprise PCIe NVMe drives deliver millions of write IOPS at zero extra cost.

👉 Read the full FinOps guide on ServerMO:

The OpenTelemetry Trap: How K8s Autoscaling Bankrupts Your Cloud Bill | ServerMO

Top comments (0)