Skip to main content
Deploy Tero Edge as a sidecar to your application to filter Prometheus metrics before they’re scraped. Edge sits between Prometheus and your application, applying policies to metrics in real-time.

How it works

Edge runs as a sidecar container in the same pod as your application. Prometheus scrapes Edge instead of your application directly. Edge proxies the request to your app’s /metrics endpoint, applies policies to filter metrics, and returns the filtered response.

Key features

  • Streaming processing: Metrics are filtered line-by-line as they stream through Edge, keeping memory usage bounded regardless of response size
  • Dual byte limits: Configure max_input_bytes_per_scrape to bound memory usage and max_output_bytes_per_scrape to cap filtered response size
  • Zero-copy forwarding: Metrics that pass policy checks are forwarded without additional allocations
  • Fail-open behavior: If policy evaluation fails, metrics pass through unchanged

Prerequisites

  • Application exposing Prometheus metrics on Kubernetes
  • Prometheus configured to scrape your pods
  • kubectl access to your cluster
  • Tero account

Connect

1

Create an Edge API key

Open your terminal and run:
tero
Navigate to Edge -> API Keys -> Create. Name your key (e.g., “Prometheus Sidecar”). Copy the key when shown—it’s only displayed once.
2

Create the secret

Store your API key as a Kubernetes secret:
kubectl create secret generic tero-edge \
  --from-literal=api-key=YOUR_API_KEY
3

Create the Edge ConfigMap

Create a ConfigMap with your Edge configuration:
tero-edge-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: tero-edge-config
data:
  config.json: |
    {
      "listen_address": "0.0.0.0",
      "listen_port": 9090,
      "upstream_url": "http://localhost:8080",
      "log_level": "info",
      "prometheus": {
        "max_input_bytes_per_scrape": 10485760,
        "max_output_bytes_per_scrape": 10485760
      },
      "policy_providers": [
        {
          "id": "tero",
          "type": "http",
          "url": "https://sync.usetero.com/v1/policy/sync",
          "headers": [
            { "name": "Authorization", "value": "Bearer ${TERO_API_KEY}" }
          ],
          "poll_interval_secs": 60
        }
      ]
    }
kubectl apply -f tero-edge-config.yaml
Set upstream_url to your application’s metrics endpoint. If your app exposes metrics on port 8080 at /metrics, use http://localhost:8080.
4

Add Edge as a sidecar

Add the Edge container to your application deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
        # Your application container
        - name: app
          image: my-app:latest
          ports:
            - name: http
              containerPort: 8080
            - name: metrics-internal
              containerPort: 8080  # Your app's metrics port

        # Tero Edge sidecar
        - name: tero-edge
          image: ghcr.io/usetero/edge-prometheus:latest
          args:
            - /etc/tero/config.json
          ports:
            - name: metrics
              containerPort: 9090  # Prometheus scrapes this port
          env:
            - name: TERO_API_KEY
              valueFrom:
                secretKeyRef:
                  name: tero-edge
                  key: api-key
          resources:
            requests:
              cpu: 50m
              memory: 32Mi
            limits:
              cpu: 200m
              memory: 128Mi
          volumeMounts:
            - name: tero-edge-config
              mountPath: /etc/tero
              readOnly: true
          livenessProbe:
            httpGet:
              path: /_health
              port: 9090
            initialDelaySeconds: 5
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /_health
              port: 9090
            initialDelaySeconds: 2
            periodSeconds: 5

      volumes:
        - name: tero-edge-config
          configMap:
            name: tero-edge-config
5

Update Prometheus scrape config

Update your Prometheus configuration to scrape the Edge sidecar port instead of your application’s metrics port:
scrape_configs:
  - job_name: "my-app"
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      # Scrape the tero-edge metrics port (9090) instead of app port
      - source_labels: [__meta_kubernetes_pod_container_name]
        action: keep
        regex: tero-edge
      - source_labels:
          [__address__, __meta_kubernetes_pod_container_port_number]
        action: replace
        regex: ([^:]+):.*
        replacement: $1:9090
        target_label: __address__
Or if using ServiceMonitor (Prometheus Operator):
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  endpoints:
    - port: metrics  # Points to Edge's port 9090
      interval: 30s
6

Verify

Check that both containers are running:
kubectl get pods -l app=my-app
Test the metrics endpoint through Edge:
kubectl exec -it <pod-name> -c tero-edge -- wget -qO- http://localhost:9090/metrics | head -20
Check Edge logs for filtering activity:
kubectl logs <pod-name> -c tero-edge --tail=50

Configuration

Prometheus settings

Configure Prometheus-specific settings in the prometheus section:
{
  "prometheus": {
    "max_input_bytes_per_scrape": 104857600,
    "max_output_bytes_per_scrape": 10485760
  }
}
SettingDefaultDescription
max_input_bytes_per_scrape10MBMaximum bytes to read from upstream per scrape. Limits memory for buffering input.
max_output_bytes_per_scrape10MBMaximum bytes to forward to client per scrape. Set lower than input if filtering reduces data.
Example: High-cardinality filtering If your application exposes 1GB of metrics but policies filter it down to 1MB, configure a high input limit with a lower output limit:
{
  "prometheus": {
    "max_input_bytes_per_scrape": 1073741824,
    "max_output_bytes_per_scrape": 10485760
  }
}
This allows Edge to process the full 1GB response while capping the filtered output at 10MB.

Policy providers

Edge supports multiple policy sources. Configure them in the policy_providers array.

File provider

Load policies from a local file. Good for static policies bundled in the ConfigMap.
{
  "id": "local",
  "type": "file",
  "path": "/etc/tero/policies.json"
}

HTTP provider

Fetch policies from a remote endpoint. Good for dynamic policies managed via the Tero API.
{
  "id": "tero",
  "type": "http",
  "url": "https://sync.usetero.com/v1/policy/sync",
  "headers": [{ "name": "Authorization", "value": "Bearer ${TERO_API_KEY}" }],
  "poll_interval_secs": 60
}
The ${TERO_API_KEY} variable is injected from the Kubernetes secret via the container environment configuration.

Memory tuning

Edge’s streaming architecture keeps memory usage predictable. Key factors:
  1. Input limit: max_input_bytes_per_scrape caps how much data Edge reads from upstream. This bounds memory for buffering input data.
  2. Output limit: max_output_bytes_per_scrape caps how much data Edge forwards to clients. Set this high if you have aggressive filtering.
  3. Line buffer: Each metric line is processed individually with a 4KB buffer. Lines exceeding this are passed through unfiltered.
  4. Concurrent scrapes: Memory scales linearly with concurrent scrapes. Each active scrape uses approximately max_input_bytes_per_scrape worst-case.
For high-cardinality workloads with aggressive filtering:
{
  "prometheus": {
    "max_input_bytes_per_scrape": 1073741824,
    "max_output_bytes_per_scrape": 52428800
  }
}
This allows processing up to 1GB of metrics while capping output at 50MB.

Troubleshooting

Prometheus can’t scrape metrics Verify Edge is running and healthy:
kubectl describe pod <pod-name>
kubectl logs <pod-name> -c tero-edge
Ensure Prometheus is configured to scrape port 9090 (Edge) not your app’s metrics port directly. Metrics not being filtered Check that policies loaded successfully:
kubectl logs <pod-name> -c tero-edge | grep -i policy
Verify your policy targets metric telemetry type with METRIC_FILTER stage. Scrapes timing out If your app has high-cardinality metrics, increase resource limits:
resources:
  limits:
    cpu: 500m
    memory: 256Mi
Also check max_input_bytes_per_scrape isn’t truncating large responses prematurely. Some metrics missing Check if scrapes are being truncated due to input or output limits:
kubectl logs <pod-name> -c tero-edge | grep -i truncat
Increase the limit if needed, or add policies to drop unwanted metrics earlier in the stream.