Exposing apps and HTTPS
Routes
Section titled “Routes”For every app with an HTTP port, the App controller creates an HTTPRoute in the environment namespace. Its hostnames are the app’s custom domains plus <app>-<environment>.<baseDomain>. Processes with protocol: tcp (databases from templates) get a cluster-internal Service only.
Without platform.baseDomain and a Gateway (platform.gatewayClassName or platform.gateway), apps are reachable only inside the cluster.
The Gateway
Section titled “The Gateway”Kuben writes the listeners of one Gateway, and only of a Gateway that is its own:
- Kuben creates it when
platform.gatewayClassNameis set (for exampletraefikon k3s, oregfor Envoy Gateway). The Gateway iskuben-system/kubenunlessplatform.gatewaynames another one, and it carries the labelkuben.dev/gateway-owner=kuben. - You dedicate an existing one by naming it in
platform.gatewayand labelling itkuben.dev/gateway-owner=kuben.
Some Gateway controllers match a listener’s port to one of their own entry points. Traefik, including the one k3s bundles, listens on 8000 and 8443, so set --set platform.gatewayPorts.http=8000 --set platform.gatewayPorts.https=8443 there. Envoy Gateway and Cilium use the listener ports directly (the default 80 and 443).
Kuben never writes a Gateway without that label. It reports why on the Gateway condition of KubenConfig (kubectl get kubenconfig kuben -o yaml): GatewayNotOwned, GatewayMissing, GatewayAPIMissing, GatewayClassNotAccepted, NotProgrammed, or Programmed with the Gateway’s address.
Listeners
Section titled “Listeners”http(port 80): with HTTPS, only the platform’s redirect route and cert-manager’s challenge routes may attach, and every other request is redirected to HTTPS. Without HTTPS, the routes of Kuben’s namespaces attach here.- One HTTPS listener per hostname, named
h-<hash>, with the certificate Secretkuben-tls-<hash>. A hostname admits routes only from the namespace that claimed it first. - Optionally one wildcard listener
httpsfor*.<baseDomain>, whenwildcardTlsSecretis set. All generated hostnames then share it.
The Gateway gets the annotation cert-manager.io/cluster-issuer, and cert-manager’s gateway-shim issues a certificate for every listener.
HTTPS is on only while the ClusterIssuer named in clusterIssuer is Ready. Kuben checks the cluster, not just the setting: until the issuer is Ready, apps are served over plain HTTP, their URLs say http://, and kuben doctor shows why.
When an address does not answer
Section titled “When an address does not answer”Each app has a Doctor (console: the app’s Doctor button; kuben status <app>; GET …/apps/{app}/doctor) that checks, in order, the GatewayClass, Kuben’s Gateway and its address, the ClusterIssuer, ports 80 and 443 of that address, the app’s route, each hostname’s certificate and DNS, and the cluster agent that delivers it. Every check that is not fine says what to do; one that could not be made is shown as unknown, never as fine. The app page’s Deployments show each rollout phase by phase, and its Logs follow the pods live.
On a server set up by kuben setup
Section titled “On a server set up by kuben setup”kuben setup --domain apps.example.com --acme-email you@example.com does all of the above on the server’s k3s: Traefik as the Gateway provider, the Gateway API CRDs and cert-manager when missing (pinned by the release’s bundle lock), a Let’s Encrypt ClusterIssuer, Kuben’s Gateway on Traefik’s entry points, and the console itself at https://kuben.apps.example.com. One wildcard DNS record, *.apps.example.com, covers the console and every app.
Dedicating an existing Gateway
Section titled “Dedicating an existing Gateway”To let Kuben create its Gateway, set --set platform.gatewayClassName=traefik and skip this section. To use a Gateway you manage yourself, create the shell and label it; Kuben fills in the listeners:
apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata: name: kuben namespace: kuben-system labels: kuben.dev/gateway-owner: kubenspec: gatewayClassName: traefik # or eg (Envoy Gateway), cilium listeners: - name: http protocol: HTTP port: 80Then reference it in the chart: --set platform.gateway=kuben-system/kuben.
A ClusterIssuer that solves HTTP-01 through that Gateway
Section titled “A ClusterIssuer that solves HTTP-01 through that Gateway”apiVersion: cert-manager.io/v1kind: ClusterIssuermetadata: name: letsencryptspec: acme: server: https://acme-v02.api.letsencrypt.org/directory email: ops@example.com privateKeySecretRef: name: letsencrypt-account solvers: - http01: gatewayHTTPRoute: parentRefs: - kind: Gateway name: kuben namespace: kuben-system sectionName: httpcert-manager needs its Gateway API support switched on: helm upgrade cert-manager … --set config.enableGatewayAPI=true.
Wildcard certificate
Section titled “Wildcard certificate”With many apps, one listener per generated hostname runs into the listener cap. Issue a DNS-01 Certificate for *.<baseDomain> whose name equals the Secret name you put in wildcardTlsSecret; cert-manager then leaves it alone instead of trying HTTP-01 on the wildcard listener.
apiVersion: cert-manager.io/v1kind: Certificatemetadata: name: apps-wildcard # == secretName namespace: kuben-systemspec: secretName: apps-wildcard dnsNames: ["*.apps.example.com"] issuerRef: kind: ClusterIssuer name: letsencrypt-dns # a DNS-01 issuerThe chart has no value for it; set it on the KubenConfig itself:
kubectl patch kubenconfig kuben --type merge -p '{"spec":{"wildcardTlsSecret":"apps-wildcard"}}'TLS per domain
Section titled “TLS per domain”Each custom domain of an app sets tls:
auto(the default): a certificate from the ClusterIssuer, on a listener of its own. Generated hostnames are alwaysauto.none: plain HTTP only. The hostname gets an HTTP listener of its own, so the platform’s HTTPS redirect does not apply to it.- the name of a Secret in the app’s namespace: the app brings its own certificate. Kuben creates a
ReferenceGrantnamed<app>-tlsthat lets the Gateway read exactly that Secret, and the listener uses it. cert-manager does not touch it.
The route carries its domains in the annotation kuben.dev/domains, so the Gateway gets the same listeners whether the App controller or the cluster’s agent wrote the route.
Honest limits
Section titled “Honest limits”- A Gateway holds at most 64 listeners; Kuben uses at most 60. Beyond that, use the wildcard listener for generated hosts. ListenerSet support (experimental in Gateway API) is on the roadmap.
- TLS termination happens at your Gateway controller. Kuben only configures it; it never handles app traffic.
- One Gateway per Kuben installation. The chart’s optional
routefor the console can use the same Gateway.
Status
Section titled “Status”Each app reports its exposure in the Exposed condition:
| Reason | Meaning |
|---|---|
RouteApplied |
the HTTPRoute exists and is attached |
NoGateway |
neither KubenConfig.spec.gatewayClassName nor spec.gateway is set; the Gateway condition of KubenConfig says more |
NoHostname |
the app has no domain and no base domain is configured |
GatewayAPIMissing |
the Gateway API CRDs are not installed |
The API reports reachability apart from health (an app can run and still be unreachable). Each app in GET …/apps and GET …/apps/{app} has an exposure object: routed says whether the Gateway accepted the route and resolved its references, and each host lists its tls mode and, for auto hosts, whether its certificate is issued (certificateReady, with cert-manager’s message while it is not).
The Check DNS button (GET …/apps/{app}/domains) tells you whether each hostname already points at the Gateway. See Custom domains and HTTPS for the user-facing flow.