0.2.12 Standalone mode with ACME certificates

Portus API & AI Gateway

A fully conformant Kubernetes gateway, built in Rust for speed. It carries your HTTP, LLM and MCP traffic on one data plane.

$helm install portus oci://ghcr.io/portus-gateway/charts/portus-gateway
Quick start →
ClientsPortusYour Services
HTTP · gRPC · TLSLLMMCP
measured 126,070 req/s · p99 0.35 ms at 30k req/s
130/130
Gateway API v1.6.2 conformance tests. No skips.
126k
Requests per second through 3 proxy pods at 256 connections
0.35ms
p99 latency at a fixed 30,000 requests per second
131Mi
Peak proxy memory across the payload benchmarks
Portus 0.2.3 on a 10-vCPU Linux VM on an Apple M4, medians of three interleaved rounds.
Conformance

Every test. Every profile. No skips.

Gateway API v1.6.2, experimental channel. The upstream suite runs in-cluster against a real per-Gateway deployment. Each square below is one passing result.

Request mirroringPath & host rewriteHeader modificationH2C & WebSocket backendsMethod & query matchingRequest & backend timeoutsHTTPRoute retryCORSListenerSetListener isolationBackendTLSPolicy with SAN validationTLS terminate, passthrough & mixedClient certificate validationTCPRouteUDPRoute
Performance

Fast under load. Light on memory.

Measured with the open gateway-api-bench method: three interleaved rounds on one machine, against the same backend pods as the top-ranked gateway in that benchmark. Every number and every round is published.

Portus 0.2.3agentgateway v1.5.0requests per second, bare GET /

p99 at 30,000 req/s

Portus0.35ms
agentgateway0.82 ms

Errors when 1 of 4 backends goes dark

Portus0.025%
agentgateway2.0 %

Peak proxy memory

Portus131Mi
agentgateway383–456 Mi

Method: the howardjohn/gateway-api-bench approach on a 10-vCPU Linux VM on an Apple M4 (k3s, pods at MTU 65485), three interleaved rounds per gateway, fortio in-cluster with one pod per rung, 3 proxy pods each. Medians shown. Numbers from one machine are only comparable with each other. Failover figures come from the gateway-api-bench suite on Docker Desktop with no retry policy; with a RetryPolicy Portus returns 0 errors. Every round, including the tests agentgateway wins →

One data plane

Three gateways. One install.

Kubernetes routes, LLM providers and MCP tool servers sit behind the same Gateway, under the same policies, in the same Rust process.

The whole Gateway API, compiled straight to protobuf.

  • Every route kind. HTTPRoute, GRPCRoute, TLSRoute, TCPRoute, UDPRoute, ListenerSet and BackendTLSPolicy.
  • A data plane per Gateway. Each Gateway gets its own Deployment, Service and PodDisruptionBudget, with its address in status.addresses.
  • Twelve policy CRDs. Timeouts, retries, rate limits, circuit breakers, health checks, CORS, IP allow lists, body limits, Basic and API-key auth.
  • No intermediate config language. The controller compiles your CRDs into one protobuf message and streams each Gateway its slice.
gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: my-gateway
spec:
  gatewayClassName: portus-gateway
  listeners:
  - name: http
    protocol: HTTP
    port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: my-route
spec:
  parentRefs:
  - name: my-gateway
  hostnames: ["app.example.com"]
  rules:
  - backendRefs:
    - name: my-service
      port: 8080
Design

Built so the hot path stays hot.

Nothing on the request path.

Keys, budgets and token checks are local lookups against state the ledger pushes in. A request never waits on another service.

Reloads never drop a connection.

Config arrives over mTLS gRPC and swaps atomically. Each data plane acknowledges the fingerprint it applied, and that drives the Gateway's Programmed condition.

The network stack is a plug-in.

Routing, policies, TLS and the AI logic live in a stack-independent core. That let us measure two engines side by side and ship the faster one.

Architecture

From CRD to wire in one compile.

Reconcilers feed a shared store, the store compiles to a CompiledConfig protobuf, and each data plane receives only its own Gateway's slice.

Kubernetes API Gateway · HTTPRoute · GRPCRoute · TLSRoute TCPRoute · UDPRoute · AIRoute · AIProvider watch portus-controller reconcilers → ConfigStore → CompiledConfig portus-ledger keys · budgets · usage gRPC stream · mTLS snapshots · usage dataplane Gateway A · rama dataplane Gateway B · rama dataplane Gateway C · pingora your services your services your services

Controller

Watches Gateway API and Portus CRDs with kube-rs, provisions a data plane per Gateway, and recompiles on any change. Reconcilers publish events to each other instead of polling.

Data plane

Builds route maps keyed by port and hostname with full Gateway API precedence. Failing endpoints are ejected passively and readmitted with back-off. SIGTERM drains in-flight requests.

Ledger

Opt-in with the AI gateway. Issues keys, keeps budgets as a shared counter synced every second, stores usage, and fetches OAuth issuers' signing keys. It never sits on the request path.

Network stack

We tested two engines. Rama won.

Everything Portus decides lives in a core that knows nothing about the proxy framework underneath. So we ran the same gateway on Rama and on Pingora, on the same machine, and measured. Rama was faster on every payload rung and used 2–2.6× less memory, so it became the default in 0.2.4.

Default since 0.2.4

Rama 0.4

Used unpatched, with Portus's own upstream connection pool. 130/130 conformance, and the AI and MCP gateways run on it.

+3–31 %throughput vs Pingora, every rung
2–2.6×less memory
Default until 0.2.3

Pingora 0.9

Cloudflare's proxy framework carried Portus through its first releases and the benchmarks above. Rama measured faster than it on every payload rung.

Rama against Pingora: a single round on the same machine, 2026-09-15. A three-round comparison ships with the next release's numbers.

Standalone

No cluster? Still Portus.

Point the data plane at one YAML file and it runs as a plain reverse proxy, with the same routing, policies and TLS it runs in Kubernetes.

  • ACME built in. Let's Encrypt certificates over http-01 or tls-alpn-01, renewed at two thirds of their lifetime.
  • Hot reload. Edit the YAML or renew a certificate and the change applies without a restart or a dropped connection.
  • Backends by name. Docker Compose services and VM hostnames, re-resolved every 30 seconds.
PORTUS_CONFIG_FILE=portus.yaml portus-dataplane
acme:
  email: ops@example.com
  cache_dir: /var/lib/portus/acme
  challenge: http-01

listeners:
  - port: 80
    protocol: HTTP
    routes:
      - hosts: ["secure.example.com"]
        redirect: {scheme: https, status_code: 301}

  - port: 443
    protocol: HTTPS
    tls:
      acme:
        domains: ["secure.example.com"]
    routes:
      - hosts: ["secure.example.com"]
        backends:
          - address: "web:8080"   # compose service
            weight: 3
          - address: "10.0.1.2:8080"
            weight: 1
Get started

Two commands to a conformant gateway.

Kubernetes 1.32+ · Helm 3.8+ · amd64 & arm64
# Gateway API CRDs, experimental channel
kubectl apply --server-side --force-conflicts \
  -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.2/experimental-install.yaml

# Portus: controller, GatewayClass, policy CRDs, mTLS material
helm install portus oci://ghcr.io/portus-gateway/charts/portus-gateway --version 0.2.12 \
  --namespace portus --create-namespace