Ingress
Platz has two layers of ingress concerns, both of which need to be set up correctly:
- The Platz install's own ingress — the HTTPS endpoint that users and bots hit to use Platz itself. Configured at install time via the chart's
ingressvalues. Covered in Installing with Helm. - Per-cluster ingress for deployments — the HTTPS endpoint each deployment gets, when its chart enables the Standard Ingress feature. Configured per cluster on the cluster's detail page. Covered here.
If your charts don't use the Standard Ingress feature (most internal services don't need to be HTTP-reachable), you can skip this page.
The flow​
When a chart with features.ingress.enabled: true is installed into a cluster, Platz injects an ingress block into the chart's Helm values. The block is constructed from the cluster's ingress_* fields plus the deployment's name and kind. The chart's templates then render the actual Ingress resource using those values.
This means:
- The Ingress resource itself is the chart author's responsibility — they need a template that respects the injected
ingress.*values. - The hostname, TLS secret name, and ingress class come from Platz.
- DNS, TLS certificate issuance, and the ingress controller are all the cluster operator's responsibility — Platz only points at them.
Per-cluster ingress configuration​
Three fields on the cluster row, editable from /admin/clusters/<id>:
ingress_domain​
The base domain for all deployments on this cluster. Examples: apps.prod.example.com, staging.internal.
When set, the deployment's hostname is constructed as either:
<deployment-name>.<ingress_domain>— when the chart'sfeatures.ingress.hostname_formatisName.<kind>-<deployment-name>.<ingress_domain>— when the chart'shostname_formatisKindAndName(the default).
For a deployment named api-prod of kind payments on a cluster with domain apps.example.com:
hostname_format | Final hostname |
|---|---|
Name | api-prod.apps.example.com |
KindAndName | payments-api-prod.apps.example.com |
For deployments where the chart has cardinality: OnePerCluster (singleton charts — see Features), the deployment name is omitted and the hostname is just <kind>.<ingress_domain>.
If ingress_domain is empty, the Standard Ingress feature is a no-op even if the chart enables it. No ingress block is injected.
ingress_class​
The ingressClassName to set on the generated Ingress resource. Examples: nginx, alb, traefik. Must match an IngressClass actually installed in that cluster — otherwise the resource is created but no controller picks it up.
If empty, Platz injects no ingressClassName, falling back to whatever the chart's template does. Usually you do want to set this explicitly.
ingress_tls_secret_name​
The name of the Kubernetes Secret that holds the TLS certificate for the cluster's ingress domain. The secret must exist in the deployment's namespace, not the chart's templates/ or the platz namespace.
Three practical options for ensuring the secret exists in every deployment namespace:
- cert-manager with a wildcard
Certificate, replicated bykubed(emberstack/kubernetes-reflector). Issue one wildcard cert at the cluster level, replicate it into every namespace tagged with the right annotation. The most operationally simple setup if you can use wildcards. - cert-manager with per-deployment
Certificategenerated by the chart. Each deployment's chart includes aCertificatetemplate; cert-manager issues a unique cert per namespace. Higher LE rate-limit pressure but doesn't need a wildcard. - By hand, e.g., copying a secret into each new namespace via an admission controller. Brittle, but it works.
If empty, the generated Ingress is HTTP-only (no tls: block). Useful for internal clusters that terminate TLS at a load balancer.
Hostname format and changing it​
hostname_format is set in the chart's features.yaml, not in Platz. Common settings:
hostname_format | Use when |
|---|---|
Name | The deployment name is already unique within the cluster, and you want short URLs. Often used for customer-facing services where <customer-name>.example.com reads better than payments-<customer-name>.example.com. |
KindAndName | The cluster has many deployments of many kinds, and prefixing with kind avoids name collisions across kinds. The default. |
Changing the hostname_format after a deployment is already running causes the next upgrade to regenerate the Ingress with the new hostname. The DNS record (if you're using external-dns) follows; if you manage DNS by hand, you'll need to repoint manually. Browser-bookmarked links to the old hostname break.
DNS​
Platz does not manage DNS. You have two reasonable options:
- external-dns — install it in the cluster pointing at your DNS provider. It watches Ingress resources and creates corresponding DNS records automatically. Probably what you want.
- A single wildcard DNS record pointing the entire
*.<ingress_domain>at the cluster's load balancer. Cheap to set up, but the cluster's load balancer needs a wildcard cert to terminate TLS, which is the cert-manager wildcard path above.
Either way, the records need to exist before deployments come up — otherwise the first request from a user races the DNS propagation.
Multiple ingress configurations on one cluster​
Currently unsupported. A cluster has exactly one ingress_domain, one ingress_class, and one ingress_tls_secret_name. If you need two ingress controllers in the same cluster (e.g., internal-facing and public-facing), you need two clusters in Platz pointing at the same Kubernetes cluster — which isn't supported either (cluster identity is the provider ID).
The workaround: stand up two Kubernetes clusters, or chain proxies (a public-facing ALB in front of a private nginx ingress, etc.).
Troubleshooting​
Deployment came up but the ingress hostname returns 404.
Check the Ingress resource in the deployment's namespace — kubectl -n <ns> get ingress. If the resource doesn't exist, the chart probably doesn't render one (look at the chart's templates/). If it exists but no controller is reconciling it, the ingressClassName is wrong.
Hostname resolves but returns "host not configured" from the load balancer.
The Ingress resource exists and has the right hostname, but the load balancer / ingress controller hasn't picked it up yet. nginx-ingress polls; ALB needs the alb.ingress.kubernetes.io/group.name annotation if you're sharing a load balancer across deployments. Wait 30 seconds and try again.
TLS handshake fails.
The ingress_tls_secret_name either doesn't exist in the deployment's namespace, or holds a certificate for the wrong hostname. cert-manager's Order and Certificate resources show the issuance status.
Hostname is right but DNS doesn't resolve.
external-dns isn't installed, or doesn't have credentials for your DNS provider, or the Ingress doesn't have the right annotations to be picked up. The annotation defaults are external-dns.alpha.kubernetes.io/hostname (which Platz doesn't set — the chart can add it via its template) or the controller picks it from the host: field on the Ingress rule (the default behaviour for most controllers).
Caveats​
- The cluster's ingress config is global to all charts on that cluster. You can't have chart A use one TLS secret and chart B use another. If you need that, the only escape hatch is per-deployment
values_overrideto inject a differentingress.tlsblock — but you lose the Standard Ingress integration. - Changing the TLS secret affects existing deployments. On their next upgrade, they'll use the new secret reference. If the new secret doesn't exist yet, the Ingress resource breaks until it does.
- Ingress class is just a string. Platz doesn't validate that an
IngressClasswith that name exists. A typo here goes unnoticed until you try to access a deployment and get nothing. - Path-based routing isn't supported. Platz assumes one hostname per deployment. If you want
/apiand/adminto route to different deployments, you need a layer of routing above Platz (or do it in the chart's own ingress template, ignoring the Standard Ingress feature).