Skip to content

Serving: Ingress vs Gateway API

By default every NextcloudInstance is exposed through a networking.k8s.io/v1 Ingress, built from spec.ingress. spec.ingress.mode lets you switch a single instance to Gateway API (gateway.networking.k8s.io) instead, which attaches an HTTPRoute to an existing Gateway rather than creating an Ingress object.

This is additive and opt-in. ingress.mode defaults to Ingress, so every instance that never sets it — which is every instance created before this field existed — keeps rendering exactly as before. Nothing about existing behavior changes unless you explicitly set mode: GatewayAPI.

When to use which

  • Ingress (default) — the operator manages ingress.className, ingress.annotations (including the cert-manager.io/cluster-issuer injection), and TLS, and the upstream chart renders a standard Ingress object. This is the right choice unless you already have a Gateway API Gateway you want this instance attached to.
  • GatewayAPI — use this when your cluster's ingress layer is a Gateway API Gateway (e.g. Envoy Gateway, Istio) rather than an Ingress controller, or when several apps need to share one Gateway's listeners.

Configuration

spec:
  ingress:
    host: cloud.example.com
    mode: GatewayAPI
    gateway:
      parentRef:
        name: public-gateway
        namespace: gateway-system # optional — omit for a same-namespace Gateway
        sectionName: https # REQUIRED whenever mode: GatewayAPI

See examples/nextcloud-with-gatewayapi.yaml for a full example.

spec.ingress.gateway.parentRef:

Field Type Required Description
name string yes Name of the Gateway this instance's HTTPRoute attaches to.
namespace string no Namespace of the Gateway, if different from this instance's namespace.
sectionName string yes, when mode: GatewayAPI Name of a specific listener on the Gateway to attach to.

Why sectionName is required

An HTTPRoute with no sectionName attaches to every listener on the referenced Gateway — including any plaintext HTTP listener. On a shared Gateway that terminates both HTTP and HTTPS (the HTTP listener commonly exists only to redirect to HTTPS), an unpinned route bypasses that redirect and serves the instance over plaintext HTTP too. Nextcloud has a single route and a single host (unlike some apps with a separate admin console), so there is no case where an unpinned route is safe here.

This is enforced twice:

  • CRD admission — NextcloudInstance and Nextcloud both carry a x-kubernetes-validations rule rejecting mode: GatewayAPI with no gateway.parentRef.sectionName, so a misconfigured CR is rejected before the operator ever reconciles it.
  • Reconcile time — NextcloudProfile defaults and NextcloudPool templates cannot carry that CRD rule (their ingress-carrying fields are free-form, to stay forward-compatible with the upstream chart), so the operator also rejects it with a clear error whenever the merged configuration — however it was assembled — reaches build_helm_values() without a pinned sectionName.

What stays the same regardless of mode

spec.ingress.enabled keeps meaning "this instance is externally exposed," independent of mode — it is not repurposed to mean "an Ingress object exists." Because of that, none of the following change based on serving mode:

  • the instance's derived URL (status.url and what other CRs — signaling and recording servers — build for it),
  • Nextcloud's reverse-proxy trust configuration (trustedProxies/forwardedForHeaders/overwriteprotocol, written into bnerd.config.php),
  • the OIDC "requires HTTPS" warning.

An instance never renders both objects

mode: GatewayAPI always forces the chart's ingress.enabled off, so an instance never has both an Ingress and an HTTPRoute at once. This holds even against spec.helm.values.ingress.enabled: true (the Layer-4 escape hatch, which normally has the highest priority and overrides everything the operator computes): with mode: GatewayAPI, that combination is rejected outright with an error rather than silently letting either side win — silently re-enabling the Ingress would recreate the exact hazard this mode exists to prevent, and silently discarding the value would ignore configuration you explicitly wrote.

Known limitation: readiness diagnostics

The operator's workload-readiness check (surfaced in status) reads the rendered Ingress object for GatewayAPI: false (default) instances. There is currently no equivalent HTTPRoute status read for GatewayAPI: true instances — this is a deliberate scope decision for the initial Gateway API support, tracked as a follow-up, not an oversight. It does not affect serving: the instance is reachable exactly the same way; only this one diagnostic signal has parity with Ingress-mode instances pending.