Envoy Gateway

Overview of the Envoy Gateway ingress in Elastx Kubernetes CaaS

This section introduces Envoy Gateway as the ingress controller in our Elastx Kubernetes CaaS service. We manage and upgrade the controller, the Gateway API CRDs and the cluster-scoped GatewayClass named eg. You create the Gateway API objects that describe your own traffic in your own namespaces.

There are companion guides for the two ways traffic typically reaches the cluster. Pick the one that matches your setup:

Standard layout: one shared Gateway per cluster

A cluster has one shared Gateway in a dedicated namespace (for example gateway) that serves routes from all your application namespaces through a single OpenStack load balancer and IP. This is the standard setup and the direct equivalent of ingress-nginx, where a single controller fronted every host in the cluster. Each application namespace opts in with a shared-gateway-access: "true" label and contributes its own HTTPRoutes, and never touches the shared Gateway.

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#DAE7EC','primaryBorderColor':'#1E343E','primaryTextColor':'#1E343E','lineColor':'#5A7A8A','clusterBkg':'#EEF3F6','clusterBorder':'#9BB3BF','edgeLabelBackground':'#FFFFFF'}}}%%
flowchart TB
    client(["Clients"]):::client --> lb["OpenStack load balancer<br/>one LB &middot; one IP"]:::lb
    lb --> gw

    subgraph gwns["gateway namespace"]
        gw["Gateway 'shared'"]:::gw
        ctp["ClientTrafficPolicy"]:::policy
        cert["TLS certificates<br/>per hostname"]:::policy
    end
    ctp -.->|attaches to| gw
    cert -.->|terminates TLS| gw

    subgraph ta["team-a namespace"]
        appa["app + HTTPRoute"]:::app
    end
    subgraph tb["team-b namespace"]
        appb["app + HTTPRoute"]:::app
    end
    gw -->|"team-a.example.com"| appa
    gw -->|"team-b.example.com"| appb

    classDef client fill:#FFFFFF,stroke:#1E343E,color:#1E343E;
    classDef lb fill:#DAE7EC,stroke:#1E343E,color:#1E343E;
    classDef gw fill:#FBBD18,stroke:#1E343E,stroke-width:2px,color:#1E343E;
    classDef policy fill:#F5F8FA,stroke:#1E343E,color:#1E343E;
    classDef app fill:#DAE7EC,stroke:#1E343E,color:#1E343E;

Each Gateway provisions its own OpenStack load balancer, so a single shared Gateway keeps your cluster on one load balancer, one IP and one ClientTrafficPolicy: the same single-entry-point model you had with ingress-nginx. TLS is terminated centrally in the gateway namespace, with one certificate per hostname (Envoy serves the right one per request by SNI). The walkthroughs below use this layout throughout. Running more than one Gateway, a separate load balancer for a single namespace via allowedRoutes.namespaces.from: Same, is a non-standard setup for the rare case that genuinely needs an isolated IP or blast radius.

What you create

In the dedicated gateway namespace (once, by whoever owns ingress):

  • Gateway: listeners, ports, protocols, TLS; allowedRoutes selecting the shared-gateway-access: "true" label.
  • ClientTrafficPolicy: controls PROXY-protocol handling, TLS parameters, timeouts. Must live in the same namespace as the Gateway.
  • TLS Certificate / Issuer (cert-manager): one certificate per hostname you serve (HTTP-01 in direct mode, DNS-01 in proxy mode).
  • Optionally, an HTTP-to-HTTPS redirect HTTPRoute on the http listener, to match ingress-nginx’s ssl-redirect behaviour for every host.

In each application namespace (per team, self-service):

  • The shared-gateway-access: "true" namespace label.
  • HTTPRoute, GRPCRoute: routing rules, attached to the shared Gateway via cross-namespace parentRefs.
  • BackendTrafficPolicy: retries, circuit breaking.
  • SecurityPolicy: JWT, OIDC, CORS.
  • BackendTLSPolicy: mTLS toward your backends.

You reference the cluster GatewayClass by its name eg from the Gateway. You do not need to create or modify any cluster-scoped resources.

Which variant fits your setup?

The OpenStack load balancer in front of Envoy runs in TCP mode in both cases. The variants differ in how the real client IP arrives at Envoy, and your ClientTrafficPolicy has to match.

  • Direct (PROXY-protocol) mode: clients connect straight to the load balancer. The load balancer is configured with PROXY protocol v2 and prepends a PROXY header carrying the real client IP. Your ClientTrafficPolicy must enable proxy-protocol parsing. See Direct (PROXY-protocol) mode.
  • Proxy (X-Forwarded-For) mode: you put your own upstream proxy (CDN, WAF, edge proxy) in front of the load balancer. That upstream injects the real client IP into X-Forwarded-For; the load balancer passes the request through unchanged. Your ClientTrafficPolicy must trust that header with the right hop count. See Proxy (X-Forwarded-For) mode.

Coming from ingress-nginx?

If you used our managed ingress-nginx, the two modes carry over directly; only the names and the resources you write have changed:

ingress-nginx Envoy Gateway When it applies
Direct mode (use-proxy-protocol: "true") Direct (PROXY-protocol) mode Clients connect straight to our load balancer. No upstream proxy. This is the default.
Proxy mode (use-forwarded-headers: "true") Proxy (X-Forwarded-For) mode You run your own CDN / WAF / edge proxy in front of the load balancer.

As before, the mode is a cluster-level setting: we provision your cluster in one mode or the other; you do not switch it from a manifest. Tell us which fits your setup and we configure the load balancer accordingly. What you do write is a ClientTrafficPolicy that matches that mode (see the two guides above).

Proxy mode requires your own upstream proxy. X-Forwarded-For mode only makes sense when a CDN, WAF, or edge proxy actually sits in front of the load balancer and injects the header. Without one, no real client IP ever reaches Envoy and your backends see only the load balancer. If clients connect directly to Elastx, use direct (PROXY-protocol) mode instead. It carries the client IP for you and needs no CDN.

Two things also changed with Kubernetes CaaS v2:

  • Ingress always enters through the load balancer. Older clusters accepted traffic directly on each worker node’s floating IP; that path is gone. DNS for your services now points at the Gateway’s load-balancer address.
  • Floating IPs are an opt-in egress feature, used for a predictable outbound source IP, not as an ingress path.

For the full move, including clusters that use floating IPs, see Migrating from ingress-nginx.

TLS

Both walkthroughs terminate TLS on the shared Gateway, with cert-manager issuing one certificate per hostname into the gateway namespace (the same namespace as the Gateway). The validation method differs by mode: direct mode uses ACME HTTP-01 (clients reach the load balancer directly), while proxy mode uses DNS-01 (public DNS points at your upstream proxy, so HTTP-01 cannot reach Envoy). If you need a guide for installing cert-manager, see Install and upgrade cert-manager.

Advanced usage

For more advanced use cases please refer to the documentation provided by each project or contact our support:


Direct (PROXY-protocol) mode

A walkthrough of setting up Envoy Gateway when your cluster’s load balancer uses PROXY protocol v2

Proxy (X-Forwarded-For) mode

A walkthrough of setting up Envoy Gateway when your traffic arrives via an upstream proxy that injects X-Forwarded-For

Migrating from ingress-nginx

How to move from managed ingress-nginx to Envoy Gateway, with or without floating IPs and in direct or proxy mode