Skip to content
Docs / dflux EdgeGuard
ContactGet started

Configuring rate limiting

dflux EdgeGuard rate-limits with token buckets keyed by source NF type, subscriber SUPI, or a Diameter dimension. Rate-limit rules are first-class entries in the control-plane store: write them as JSON, hot-reload, and watch denials through Prometheus and the structured decision logs.

The token-bucket model

Each rate-limit rule is a token bucket with two rate knobs plus a key:

  • rps — tokens added per second (the steady-state limit).
  • burst — bucket capacity (peak traffic accepted instantly).
  • a key that selects what gets its own bucket — see Choose the right key.

A request consumes one token. If the bucket is empty, the request is denied — 429 on SBI, DIAMETER_AUTHORIZATION_REJECTED (5003) on Diameter — and the denial increments the edge_rate_limit_exceeded_total counter and is written to the structured decision log.

Write a rule

A limit at 100 RPS, burst 200, scoped to traffic from UDM consumers:

JSON

Deploy it:

Bash

d3x-edgectl talks to the admin gRPC surface on 127.0.0.1:9091 by default. See the d3x-edgectl reference for auth and the -addr flag.

Choose the right key

source_nf_type scopes which traffic a rule applies to (by source NF type). The bucket dimension — what gets its own counter — is set separately:

  • key_by_supi: true — one bucket per subscriber SUPI. Use this so a single misbehaving UE can't drown out other subscribers.
  • key — selects an explicit bucket dimension. One of: global (a single cluster-wide bucket), diameter_origin_host, diameter_origin_realm, diameter_app_id, diameter_command_code, or imsi_prefix:N (the first N IMSI digits — e.g. imsi_prefix:6 is MCC+MNC).
  • neither set — the legacy default: one bucket per source IP on SBI, per Origin-Host on Diameter.

An unknown key value is a load-time error, so a typo fails the reload rather than silently passing traffic.

Watch bucket cardinality
A per-SUPI bucket holds state per active subscriber. Millions of active SUPIs means millions of buckets — memory grows with traffic. A background sweep runs every minute and evicts buckets idle for more than five minutes, but spikes can still pressure memory. Profile in staging before deploying key_by_supi limits at scale.

Multiple limits on the same traffic

Each entry in rate_limits[] becomes a separate filter in the request chain. A request that matches two rules consumes a token from both buckets and is denied by whichever is tighter.

Layered protection looks like:

  1. A global cap to protect producers in aggregate.
  2. A per-source-NF-type cap to bound any single consumer class.
  3. A key_by_supi cap for hot-subscriber protection.

Observe

Two Prometheus series tell the rate-limiting story:

  • edge_rate_limit_exceeded_total{rule="…"} — counter, ticks every time a bucket denies a request, labelled by rule name.
  • edge_filter_decisions_total{filter="ratelimit:<rule>", decision="deny"} — the same denial seen through the generic filter-decision counter (the filter label is the rule's filter name, ratelimit: plus the rule name).

Alert on edge_rate_limit_exceeded_total rising sharply — that's either a real attack or a legitimate workload outgrowing its cap. Each denial is also logged as a structured JSON record carrying request_id, source_nf, target_nf, the bucket key, and the rule name, so you can slice denials by source or subscriber in your log pipeline before reflexively raising the limit.

Hot reload

Rate-limit rules hot-reload like every other control-plane mutation — the filter chain rebuilds and the new bucket set takes effect on the next request. See Hot reload and runtime ops for the reload guarantee and how in-flight buckets carry over or reset.

Where to go next