Enforcing per-user limits without a control-plane API
Composed Marzban, Xray, nginx, and supervisord into a single Kubernetes pod, with a Python device-limiter that enforces per-user limits by parsing access logs — no upstream API required.
- Single-pod relay design
- Per-user limits without upstream API support
- Resilient to common network disruption
Context
This one sits at the networking end of my work rather than the application end. The need was a relay that could be stood up quickly, survive the kinds of network disruption it would predictably face, and enforce per-user usage limits — even though the control tooling didn’t expose an API for the thing I needed to control.
Constraints
- A missing control-plane API. The upstream limit-enforcement I wanted simply wasn’t exposed, so the obvious integration point didn’t exist.
- A hostile network environment. The relay had to resist common disruption rather than assume a stable, friendly path to the internet.
- Fast to deploy, cheap to run. Getting a reliable, bounded service up with minimal moving parts.
Approach
I composed the relay from established pieces — Marzban, Xray, nginx, and supervisord — into a single Kubernetes pod, with supervisord keeping the in-pod processes healthy and nginx terminating and shaping traffic. The interesting part was enforcement: since the upstream offered no API for per-user limits, I wrote a small Python device-limiter that parses Xray’s access logs and enforces limits from observed traffic instead of from a control plane that didn’t exist.
┌────────── single K8s pod ──────────┐
│ nginx (traffic termination) │
│ │ │
│ xray (protocol handling) │
│ │ ↑ │
│ access logs ──▶ py-limiter │
│ (observed traffic → enforcement) │
│ supervisord (process health) │
└────────────────────────────────────┘
Decision — derive enforcement from logs, not from an absent API. The clean solution would have been a control-plane call; it wasn’t on the table. Reading the behavior the system already emits — its access logs — turned an unsupported requirement into a tractable one. It’s a worse interface than an API, but it’s a real one, and it shipped.
Decision — single pod over a microservice split. For a relay of this scope, spreading the components across services would have added orchestration and failure surface for no benefit. Co-locating them in one pod under supervisord kept the deployment a single, legible unit that’s trivial to reason about and restart.
Outcome
The relay runs as a compact Kubernetes deployment that keeps usage bounded and stays up through the disruption it was built to expect — all without requiring any change to the upstream tooling. It’s a reminder that a lot of infrastructure work is making a constraint someone else imposed into something you can actually operate.
What I’d revisit
Log-parsing enforcement is pragmatic but brittle: it’s coupled to a log format I don’t own. I’d add a thin schema check that fails loudly if the upstream log format shifts, so enforcement degrades into an alert rather than silently stopping the day a format changes.