Kgateway

The Kgateway Emitter supports generating Gateway API and kgateway resources from Ingress manifests using:

  • Provider: ingress-nginx

Note: All other providers are ignored by the emitter.

Usage

Ingress2gateway reads Ingress resources from a Kubernetes cluster or a file. It outputs the equivalent Gateway API and kgateway-specific resources in YAML or JSON format to stdout. The simplest case is to convert all ingresses from the ingress-nginx provider:

ingress2gateway print --providers=ingress-nginx --emitter=kgateway

This command:

  1. Reads the kubeconfig file to extract cluster credentials and the current active namespace.
  2. Searches for ingress-nginx resources in that namespace.
  3. Converts them to Gateway API resources (currently only Gateways and HTTPRoutes).

Options

print command

Flag Default Value Required Description
all-namespaces False No If present, list the requested object(s) across all namespaces. Namespace in the current context is ignored even if specified with –namespace.
input-file No Path to the manifest file. When set, the tool reads ingresses from the file instead of from the cluster. Supported files are yaml and json.
namespace No If present, the namespace scope for the invocation.
output yaml No The output format, either yaml or json.
providers Yes Comma-separated list of providers (only ingress-nginx is supported in this downstream).
emitter standard No The emitter to use for generating Gateway API resources (supported values: standard, kgateway).
kubeconfig No The kubeconfig file to use when talking to the cluster. If the flag is not set, a set of standard locations can be searched for an existing kubeconfig file.

Conversion of Ingress resources to Gateway API

Processing Order and Conflicts

Ingress resources are processed in a defined order to ensure deterministic Gateway API configuration. This order also determines precedence of Ingress resources and routes in case of conflicts.

Ingress resources with the oldest creation timestamp are sorted first and therefore given precedence. If creation timestamps are equal, sorting is done based on the namespace/name of the resources. If an Ingress rule conflicts with another (e.g. same path match but different backends), an error is reported for the one that sorts later.

Since the Ingress v1 spec does not define conflict resolution, this tool adopts the following rules, which are similar to the Gateway API conflict resolution guidelines.

Ingress resource fields to Gateway API fields

Given a set of Ingress resources, ingress2gateway generates a Gateway with various HTTP and HTTPS listeners as well as HTTPRoutes that represent equivalent routing rules.

Ingress Field Gateway API configuration
ingressClassName If configured on an Ingress resource, this value is translated to kgateway.
defaultBackend If present, this configuration generates a Gateway Listener with no hostname specified as well as a catchall HTTPRoute that references this listener. The backend specified here is translated to a HTTPRoute rules[].backendRefs[] element.
tls[].hosts Each host in an IngressTLS results in a HTTPS Listener on the generated Gateway with the following: listeners[].hostname = host as described, listeners[].port = 443, listeners[].protocol = HTTPS, listeners[].tls.mode = Terminate
tls[].secretName The secret specified here is referenced in the Gateway HTTPS Listeners mentioned above with the field listeners[].tls.certificateRefs. Each Listener for each host in an IngressTLS gets this secret.
rules[].host If non-empty, each distinct value for this field in the provided Ingress resources results in a separate Gateway HTTP Listener with matching listeners[].hostname. listeners[].port is set to 80 and listeners[].protocol to HTTPS. In addition, Ingress rules with the same hostname generate HTTPRoute rules in a HTTPRoute with hostnames containing it as the single element. If empty, similar to the defaultBackend, a Gateway Listener with no hostname configuration is generated (if it doesn’t exist) and routing rules are generated in a catchall HTTPRoute.
rules[].http.paths[].path This field translates to a HTTPRoute rules[].matches[].path.value configuration.
rules[].http.paths[].pathType This field translates to a HTTPRoute rules[].matches[].path.type configuration. Ingress Exact = HTTPRoute Exact match. Ingress Prefix = HTTPRoute PathPrefix match.
rules[].http.paths[].backend The backend specified here is translated to a HTTPRoute rules[].backendRefs[] element.

Supported Annotations

Traffic Behavior

  • nginx.ingress.kubernetes.io/client-body-buffer-size
  • nginx.ingress.kubernetes.io/proxy-body-size
  • nginx.ingress.kubernetes.io/enable-cors
  • nginx.ingress.kubernetes.io/cors-allow-origin
  • nginx.ingress.kubernetes.io/cors-allow-credentials
  • nginx.ingress.kubernetes.io/cors-allow-headers
  • nginx.ingress.kubernetes.io/cors-expose-headers
  • nginx.ingress.kubernetes.io/cors-allow-methods
  • nginx.ingress.kubernetes.io/cors-max-age
  • nginx.ingress.kubernetes.io/limit-rps
  • nginx.ingress.kubernetes.io/limit-rpm
  • nginx.ingress.kubernetes.io/limit-burst-multiplier
  • nginx.ingress.kubernetes.io/proxy-send-timeout
  • nginx.ingress.kubernetes.io/proxy-read-timeout
  • nginx.ingress.kubernetes.io/ssl-redirect: When set to "true", adds a RequestRedirect filter to HTTPRoute rules that redirects HTTP to HTTPS with a 301 status code. Note that ingress-nginx redirects with code 308, but that isn’t supported by gateway API.
  • nginx.ingress.kubernetes.io/force-ssl-redirect: When set to "true", adds a RequestRedirect filter to HTTPRoute rules that redirects HTTP to HTTPS with a 301 status code. Treated identically to ssl-redirect. Note that ingress-nginx redirects with code 308, but that isn’t supported by gateway API.
  • nginx.ingress.kubernetes.io/ssl-passthrough: When set to "true", enables TLS passthrough mode. Converts the Ingress to a TLSRoute with a Gateway listener using protocol: TLS and tls.mode: Passthrough. The HTTPRoute that would normally be created is removed.
  • nginx.ingress.kubernetes.io/use-regex: When set to "true", indicates that the paths defined on an Ingress should be treated as regular expressions. Uses host-group semantics: if any Ingress contributing rules for a given host has use-regex: "true", regex-style path matching is enforced on all paths for that host (across all contributing Ingresses).
  • nginx.ingress.kubernetes.io/rewrite-target: Rewrites the request path using regex rewrite semantics. Uses host-group semantics: if any Ingress contributing rules for a given host sets rewrite-target, regex-style path matching is enforced on all paths for that host (across all contributing Ingresses), consistent with ingress-nginx behavior.

Backend Behavior

  • nginx.ingress.kubernetes.io/proxy-connect-timeout: Sets the timeout for establishing a connection with a proxied server. It should be noted that this timeout cannot usually exceed 75 seconds.
  • nginx.ingress.kubernetes.io/load-balance: Sets the algorithm to use for load balancing to a proxied server. The only supported value is round_robin.
  • nginx.ingress.kubernetes.io/affinity: Enables session affinity (only “cookie” type is supported). Maps to BackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies.
  • nginx.ingress.kubernetes.io/session-cookie-name: Specifies the name of the cookie used for session affinity. Maps to BackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies[].cookie.name.
  • nginx.ingress.kubernetes.io/session-cookie-path: Defines the path that is set on the cookie. Maps to BackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies[].cookie.path.
  • Note (regex-mode constraint): Ingress NGINX session cookie paths do not support regex. If regex-mode is enabled for a host (via use-regex: "true" or rewrite-target) and cookie affinity is used, session-cookie-path must be set; the provider validates this and emits an error if it is missing.
  • nginx.ingress.kubernetes.io/session-cookie-domain: Sets the Domain attribute of the sticky cookie. Note: This annotation is parsed but not currently mapped to kgateway as the Cookie type doesn’t support domain.
  • nginx.ingress.kubernetes.io/session-cookie-samesite: Applies a SameSite attribute to the sticky cookie. Browser accepted values are None, Lax, and Strict. Maps to BackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies[].cookie.sameSite.
  • nginx.ingress.kubernetes.io/session-cookie-expires: Sets the TTL/expiration time for the cookie. Maps to BackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies[].cookie.ttl.
  • nginx.ingress.kubernetes.io/session-cookie-max-age: Sets the TTL/expiration time for the cookie (takes precedence over session-cookie-expires). Maps to BackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies[].cookie.ttl.
  • nginx.ingress.kubernetes.io/session-cookie-secure: Sets the Secure flag on the cookie. Maps to BackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies[].cookie.secure.
  • nginx.ingress.kubernetes.io/service-upstream: When set to "true", configures Kgateway to route to the Service’s cluster IP (or equivalent static host) instead of individual Pod IPs. For each covered Service, the emitter creates a Backend resource with spec.type: Static and rewrites the corresponding HTTPRoute.spec.rules[].backendRefs[] to reference that Backend (group gateway.kgateway.dev, kind Backend).
  • nginx.ingress.kubernetes.io/backend-protocol: Indicates the L7 protocol that is used to communicate with the proxied backend.
    • Supported values (mapped): GRPC, GRPCS
      • If service-upstream: "true" is also set for the same Service backend, the emitter sets spec.static.appProtocol: grpc on the generated Backend.
      • Otherwise, the emitter does not create or modify Kubernetes Service resources. Instead, it emits an INFO notification with a kubectl patch command to update the existing Service port with appProtocol: grpc.
    • Values treated as default HTTP/1.x (no-op): HTTP, HTTPS, AUTO_HTTP
    • Unsupported values (rejected by provider): FCGI (and others)
    • Safety note: Because emitting Service manifests could overwrite user-managed Service configuration, ingress2gateway intentionally avoids generating Service resources for this annotation.

External Auth

  • nginx.ingress.kubernetes.io/auth-url: Specifies the URL of an external authentication service.
  • nginx.ingress.kubernetes.io/auth-response-headers: Comma-separated list of headers to pass to backend once authentication request completes.

Basic Auth

  • nginx.ingress.kubernetes.io/auth-type: Must be set to "basic" to enable basic authentication. Maps to TrafficPolicy.spec.basicAuth.
  • nginx.ingress.kubernetes.io/auth-secret: Specifies the secret containing basic auth credentials in namespace/name format (or just name if in the same namespace). Maps to TrafficPolicy.spec.basicAuth.secretRef.name.
  • nginx.ingress.kubernetes.io/auth-secret-type: Only "auth-file" is supported (default). The secret must contain an htpasswd file in the key "auth". Only SHA hashed passwords are supported. Maps to TrafficPolicy.spec.basicAuth.secretRef.key set to "auth".

Backend TLS

  • nginx.ingress.kubernetes.io/proxy-ssl-secret: Maps to BackendConfigPolicy.spec.tls.secretRef
  • nginx.ingress.kubernetes.io/proxy-ssl-verify: Maps to BackendConfigPolicy.spec.tls.insecureSkipVerify (inverted: "on" = false, "off" = true)
  • nginx.ingress.kubernetes.io/proxy-ssl-name: Maps to BackendConfigPolicy.spec.tls.sni (automatically enables SNI)

Access Logging

  • nginx.ingress.kubernetes.io/enable-access-log: If enabled, creates an HTTPListenerPolicy that configures a basic policy for Envoy access logging. Maps to HTTPListenerPolicy.spec.accessLog[].fileSink. This can be further customized as needed, see docs.

Regex Path Matching and Rewrites

  • use-regex and rewrite-target may mutate HTTPRoute path matching for a host:

    • When regex-mode is enabled for a host, the emitter converts all PathPrefix/Exact matches under that host to RegularExpression matches.
    • For Ingresses that set use-regex: "true", their contributed path strings are treated as regex (not escaped as literals).
    • For other Ingresses under the same host (that did not set use-regex: "true"), their contributed path strings are treated as literals within a regex match (escaped), to preserve the original non-regex intent.
  • rewrite-target generates TrafficPolicy URL rewrite:

    • For each rule covered by an Ingress that sets rewrite-target, the emitter creates a per-rule TrafficPolicy named:

      • <ingress-name>-rewrite-<rule-index>
    • That policy sets:

      spec:
        urlRewrite:
          pathRegex:
            pattern: <regex pattern derived from the HTTPRoute rule match>
            substitution: <rewrite-target value>
    • The policy is attached via ExtensionRef filters to only the covered backendRefs (partial coverage), rather than using targetRefs.

TrafficPolicy Projection

Annotations in the Traffic Behavior category are converted into TrafficPolicy resources.

These policies are attached using:

  • targetRefs when the policy applies to all backends, or
  • extensionRef backend filters for partial coverage.

Examples:

  • Body size annotations control spec.buffer.maxRequestSize
  • Rate limit annotations control spec.rateLimit.local.tokenBucket
  • Timeout annotations control spec.timeouts.request or streamIdle
  • SSL redirect annotations add RequestRedirect filters to HTTPRoute rules
  • SSL passthrough annotation converts HTTPRoute to TLSRoute with TLS passthrough Gateway listener

BackendConfigPolicy Projection

Annotations in the Backend Behavior category are converted into BackendConfigPolicy resources.

Currently supported:

  • proxy-connect-timeout: Maps to BackendConfigPolicy.spec.connectTimeout
  • Session affinity annotations: Maps to BackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies with cookie-based hash policy

If multiple Ingresses target the same Service with conflicting proxy-connect-timeout values, the lowest timeout wins and a warning is emitted.

TLSRoute Projection

Annotations that require TLS passthrough mode are converted into TLSRoute resources instead of HTTPRoute resources.

Currently supported:

  • nginx.ingress.kubernetes.io/ssl-passthrough:
    • When enabled, the Ingress is converted to a TLSRoute resource
    • A Gateway listener is created with protocol: TLS and tls.mode: Passthrough
    • The listener uses port 443 (when hostname is specified) or 8443 (default)
    • The HTTPRoute that would normally be created is removed
    • Backend services must handle TLS termination themselves

Backend Projection

Annotations that change how upstreams are represented (rather than how they are load balanced or configured) can be projected into Kgateway Backend resources.

Currently supported:

  • nginx.ingress.kubernetes.io/service-upstream:
    • For each Service backend covered by an Ingress with service-upstream: "true", the emitter creates a Backend with:
      • spec.type: Static
      • spec.static.hosts containing a single {host, port} entry derived from the Service (e.g. myservice.default.svc.cluster.local:80).
    • Matching HTTPRoute.spec.rules[].backendRefs[] are rewritten to reference this Backend instead of the core Service.
  • nginx.ingress.kubernetes.io/backend-protocol:
    • When set to GRPC or GRPCS and service-upstream: "true" is set for the same backend, the emitter stamps spec.static.appProtocol: grpc on the generated Backend.
    • When set to GRPC or GRPCS without service-upstream: "true", the emitter emits an INFO notification that includes a kubectl patch service ... command to set spec.ports[].appProtocol on the existing Service.
    • HTTP, HTTPS, and AUTO_HTTP are treated as default HTTP/1.x behavior and do not emit additional config.

Summary of Policy Types

Annotation Type Kgateway Resource
Request/response behavior TrafficPolicy
Upstream connection behavior BackendConfigPolicy
Upstream representation. Backend
TLS passthrough TLSRoute

Limitations

  • Only the ingress-nginx provider is currently supported by the Kgateway emitter.
  • Some NGINX behaviors cannot be reproduced exactly due to Envoy/Kgateway differences.
  • Regex-mode is implemented by converting HTTPRoute path matches to RegularExpression. Some ingress-nginx details (such as case-insensitive ~* behavior) may not be reproduced exactly depending on the underlying Gateway API/Envoy behavior and the patterns provided.

Supported but not translated Annotations

The following annotations have equivalents in kgateway but are not (as of yet) translated by this tool.

nginx.ingress.kubernetes.io/auth-proxy-set-headers

Supported in TrafficPolicy

spec:
  extAuth:
    httpService:
      authorizationRequest:
        headersToAdd:
        - key: x-forwarded-host
          value: "%DOWNSTREAM_REMOTE_ADDRESS%"