Skip to content

Envoy Gateway

Envoy Gateway v1.6.3 provides HTTP routing and ingress using the Kubernetes Gateway API.

Architecture

Envoy Gateway deploys Envoy Proxy instances that handle incoming traffic:

  • envoy-internal -- Private network access (split DNS)
  • envoy-external -- Public access via Cloudflare Tunnel

Configuration

kubernetes/apps/network/envoy-gateway/
├── app/
│   ├── helmrelease.yaml      # Envoy Gateway operator
│   ├── ocirepository.yaml    # Chart source
│   ├── certificate.yaml      # TLS wildcard certificate
│   ├── envoy.yaml            # Gateway config, policies, responseOverride
│   ├── grafanadashboard.yaml # Monitoring dashboard
│   ├── podmonitor.yaml       # Prometheus metrics
│   └── kustomization.yaml
├── error-pages/
│   ├── helmrelease.yaml      # Error pages service (app-template)
│   ├── ocirepository.yaml    # Chart source
│   └── kustomization.yaml
└── ks.yaml                   # Flux Kustomizations (gateway + error-pages)

Exposing Applications

Internal Only

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: my-app
spec:
  parentRefs:
    - name: envoy-internal
      namespace: network
  hostnames:
    - "my-app.example.com"
  rules:
    - backendRefs:
        - name: my-app
          port: 80

Public (via Cloudflare)

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: my-app
spec:
  parentRefs:
    - name: envoy-external
      namespace: network
  hostnames:
    - "my-app.example.com"
  rules:
    - backendRefs:
        - name: my-app
          port: 80

SSO Integration

Envoy Gateway supports OIDC SecurityPolicy for Kanidm SSO:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: kanidm-oidc
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: <app-route>
  oidc:
    provider:
      issuer: "https://idm.example.com/oauth2/openid/<client>"
    clientID: "<client-name>"
    clientSecret:
      name: "<secret-name>"

Custom Error Pages

Error Pages provides styled error responses for all routes behind both gateways. When a backend returns an error, the BackendTrafficPolicy responseOverride redirects the client to the error-pages service.

Handled status codes: 403, 404, 500, 502, 503, 504

The responseOverride in the BackendTrafficPolicy (in envoy.yaml) uses redirect rules:

responseOverride:
  - match:
      statusCodes:
        - type: Value
          value: 404
    redirect:
      scheme: https
      hostname: error.example.com
      path:
        type: ReplaceFullPath
        replaceFullPath: /404.html
      statusCode: 302

This applies globally to all routes behind envoy-internal and envoy-external since the policy targets all Gateways via targetSelectors.

Template: The error-pages service uses the "hacker-terminal" template with SHOW_DETAILS=true.

Per-Route BackendTrafficPolicy

The global BackendTrafficPolicy (compression, error page redirects) targets all Gateways. Individual applications can override this by attaching a BackendTrafficPolicy to their HTTPRoute:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: my-app-backend
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: my-app
  # Override specific settings — omitted fields use defaults, NOT the gateway policy
  connection:
    bufferLimit: 16Mi
  timeout:
    http:
      requestTimeout: 0s

When a route-level policy exists alongside a gateway-level policy, the route-level policy wins for that route. All other routes continue using the gateway-level policy.

LibreSpeed speed test

The LibreSpeed deployment uses this pattern to disable compression and error page redirects on speed test routes, since compressing random data wastes CPU and error page matching adds overhead.

ClientTrafficPolicy is gateway-only

Unlike BackendTrafficPolicy, ClientTrafficPolicy cannot target HTTPRoute — only Gateway (optionally scoped to a listener via sectionName). Client-side settings (HTTP/2 windows, TLS, HTTP/3) always apply to all routes on a listener.

Monitoring

  • Grafana dashboard enabled via GrafanaOperator
  • PodMonitor exports metrics to Prometheus