feat!(networking): configurable ingress host address and group networking related config options under common networking key#6699
Conversation
…rt and group `publicHosts`, `ingressHosts`, `subdomainSeparator`, `enforceHTTPS`, and `traefikVersion` under this common group for improved flexibility.
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR restructures Helm chart configuration related to networking by moving URL/host/ingress-related values under a new .Values.networking object and updating templates/helpers to consume the new structure.
Changes:
- Introduces
.Values.networkingwithpublicHosts,ingressHosts,subdomainSeparator,enforceHTTPS, andtraefikVersion. - Updates multiple Helm templates/helpers to compute hosts/URLs from
.Values.networking.*and optional ingress host overrides. - Updates deployment tooling (
deploy.py) to setnetworking.publicHostsinstead of the previous top-levelpublic.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| kubernetes/loculus/values.yaml | Moves enforceHTTPS, traefikVersion, and subdomainSeparator under networking. |
| kubernetes/loculus/values.schema.json | Adds schema for networking (publicHosts/ingressHosts + related fields) and removes old top-level keys. |
| kubernetes/loculus/templates/lapis-ingress.yaml | Uses networking.ingressHosts + networking.subdomainSeparator + networking.traefikVersion + networking.enforceHTTPS. |
| kubernetes/loculus/templates/keycloak-config-map.yaml | Switches Keycloak host/redirect URIs to use networking.ingressHosts overrides. |
| kubernetes/loculus/templates/ingressroute.yaml | Uses networking.* and introduces overridable website/backend/keycloak/minio ingress hosts. |
| kubernetes/loculus/templates/ingest-config.yaml | Uses the shared loculus.backendUrl helper for server backend URL. |
| kubernetes/loculus/templates/ena-submission-config.yaml | Uses the shared loculus.backendUrl helper for server backend URL. |
| kubernetes/loculus/templates/docs-preview.yaml | Uses networking.ingressHosts.docs override for docs hostname. |
| kubernetes/loculus/templates/autoapprove-config.yaml | Uses the shared loculus.backendUrl helper for server backend URL. |
| kubernetes/loculus/templates/_urls.tpl | Updates URL helpers to read from networking.publicHosts and networking.ingressHosts. |
| kubernetes/loculus/templates/_common-metadata.tpl | Updates runtime config generation to use networking.publicHosts and networking.ingressHosts. |
| deploy.py | Updates Helm --set-json path from public to networking.publicHosts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ng.subdomainSeparator
…when values are not set and improve URL construction
|
@florianzwagemaker I've started looking at this and have a question regarding your deployment needs. Do you just need to escape the hardcoded prefixes and allow setting urls independently or do you genuinely need public hosts to be entirely different from ingress host? If it's just the former we could reduce complexity compared to the latter. Maybe a a bit more context about your intended deployment environment could help us understand requirements better and keep things as simple as possible. |
| {{- else if $ingressHosts.backend -}} | ||
| {{- printf "https://%s" $ingressHosts.backend -}} |
There was a problem hiding this comment.
In the PR description you write:
This means that the ingressHosts keys will only be used if a publicHosts key is set for the same service.
IIUC ingressHost overwrites are also used if publicHosts are empty here? Maybe I'm misunderstanding the line from the PR desc?
There was a problem hiding this comment.
You're correct! that sentence in my original PR description was effectively the inverse of what is happening and my wording was rather confusing.
What is actually happening is that ingressHosts is only used in the URL templates if publicHosts is not set
In other words, a key for ingressHosts is only used as a fallback for a specific service if no publicHosts key is set.
I've updated the PR description to properly reflect this and added a per-service table of combinations of set keys in publicHosts and ingressHosts and the resulting runtime URLs and ingress routes, hopefully these examples make it easier to see what the result will be of various combinations of set keys / overrides
…tually contains the websiteUrl that end-users will interact with
|
First of all my apologies for the delayed response.
The primary goal for us was to escape the hardcoded prefixes. However, that would only serve the deployment goal that we have today. I agree that it adds some initial schema layout which is not ideal, but i think this reduces the deployment complexity for admins in various cloud environments and I'd argue that separating these values is practically unavoidable in a longer term. A client-facing URL and what a cluster routes on serve two very different purposes and tightly coupling these two would require loads of workarounds like custom proxy rewrites or DNS hacks just to make the chart work in more locked-down environments. Making these independently configurable makes the chart more agnostic to the network in which it will live. As for our own direct deployment(s), there are some aspects that would be made significantly easier by separating ingress from client-facing urls. Two that directly come to mind are multi-instance deployments (e.g. multiple deployments that share one keycloak instance for authentication), and our hybrid (cloud & on-premise) environment that have to deal with internal firewalls. Both of which would benefit greatly from separated ingress and public-facing url configurations. Most of the added template complexity comes from the resolution chain ensuring the automatic fallback to the computed defaults if the various keys are left empty. Which should keep things mostly straightforward for standard deployments while also accommodating more advanced use-cases/environments. |
| {{- printf "https://%s" $ingressHosts.website -}} | ||
| {{- else if eq $.Values.environment "server" -}} | ||
| {{- (printf "https://%s" $.Values.host) -}} | ||
| {{- (printf "https://%s" (.Values.host | default "")) -}} |
There was a problem hiding this comment.
Not sure we need those changes replacing $.Values.host with .Values.host - seems unnecessary for this PR
There was a problem hiding this comment.
Thanks for the catch, going to .Values.host from $.Values.host is indeed not needed and only cosmetic.
I'll change this back throughout the modified files (this pattern is also used in _common_metadata.tpl for example)
When looking closer now though, most of the | default "" fallbacks can be safely removed now as well. These were initially added as guardrails when i was working on the the resolution chain, but the if/else branching already provides sufficient safety (both were added in the same commit here)
The one exception is the line in ingressroute.yml where the default fallback serves as a guardrail for the hasPrefix check.
https://github.com/florianzwagemaker/loculus/blob/e988e2317a3a1d79783b6f8fb94f12cb2fa86609/kubernetes/loculus/templates/ingressroute.yaml#L52
I'll clean both up in the next commit
updated in e988e23
- revert `$.Values.host` -> `.Values.hos` changes - Remove redundant `| default ""` fallbacks in URL construction (except for ingressroute.yaml `hasPrefix` guard)
This PR modifies how networking and public-facing URLs are constructed for Loculus deployments.
New
ingressHostskey in the helm chartThis modification allows for changing of the hostnames for each service Ingress rule (for each public facing service).
Every Ingress host rule was computed from
.Values.hostand.Values.subdomainSeparatorwith a predefined (hardcoded) prefix.i.e.
backend.host.xyz,lapis.host.xyz, orauthentication.host.xyz.The default behavior should be okay for the majority of deployments, however in some environments there might be a requirement for different hostnames either due to security policies, predefined hostname standards or potentially multiple deployments.
This change makes this optionally configurable to allow for compatibility with these potential usecases by introducing a new
ingressHostskey with properties for each ingress resource, allowing ingress hostnames to be set independently of the public urls. An example can be found below.networkingkey and moving of several properties to this groupWith the introduction of the
ingressHostsproperties i figured it would be neater to group all keys together that are involved in URLs and hostnames. Including the keys that are used in the automatic construction of urls. The moved properties arepublic(here renamed topublicHosts),subdomainSeparator,enforceHTTPS, andtraefikVersion.Resolution chain
Both
publicHostsandingressHostscan be set independently for different purposes:publicHostscontrols the client-facing URLs baked into runtime config (what browsers/clients use), whileingressHostscontrols the KubernetesIngresshost rules (what the cluster routes on). They can be set together or independently.In the URL templates (
_urls.tpl,_common-metadata.tpl), the resolution chain for each service is:This means that in the URL templates,
ingressHostsis used as a fallback when nopublicHostskey is set for the same service.The
ingressHostskeys are always used independently in the ingress route templates (ingressroute.yaml,lapis-ingress.yaml,docs-preview.yaml) to control the actual Ingress host rules, regardless of whetherpublicHostsis set.If
ingressHostsis kept empty then both the URL templates and the ingress route templates fall back to the previously computed default values based onsubdomainSeparator+host.This makes the logic mostly backwards compatible, but i figured it would be better to consider this a breaking change as the moved keys for public urls would require existing deployments to change the helm chart.
Per-service configuration reference
The tables below show the resulting values for each service across all combinations of
publicHostsandingressHosts.to keep the tables a bit more readable, the
networking.prefix is omitted from the column headers, sopublicHosts.backendUrlrefers tonetworking.publicHosts.backendUrlandingressHosts.backendrefers tonetworking.ingressHosts.backend.The "Runtime URL" column is what gets baked into client-facing runtime config (full URL with scheme); the "Ingress host" column is the bare hostname used in the Kubernetes
Ingressresource (no scheme).Backend
(relevant files:
_urls.tpl,ingressroute.yaml)publicHosts.backendUrlingressHosts.backendbackendUrlhttps://backend<sep><host>backend<sep><host>api.cluster.localhttps://api.cluster.localapi.cluster.localhttps://api.public.comhttps://api.public.combackend<sep><host>https://api.public.comapi.cluster.localhttps://api.public.comapi.cluster.localWebsite
(relevant files:
_urls.tpl,ingressroute.yaml,keycloak-config-map.yaml)The
loculus.websiteUrltemplate is also used to derive the Keycloakbackend-clientredirectUris(and by extension the CORSwebOrigins, since Keycloak derives them from redirect URIs). This ensures that after login, Keycloak redirects the user back to the website URL that the client actually uses and not the ingress hostname.publicHosts.websiteUrlingressHosts.websitewebsiteUrlredirectUrishttps://<host><host>(+www.<host>)https://<host>/*frontend.cluster.localhttps://frontend.cluster.localfrontend.cluster.localhttps://frontend.cluster.local/*https://loculus.public.comhttps://loculus.public.com<host>(+www.<host>)https://loculus.public.com/*https://loculus.public.comfrontend.cluster.localhttps://loculus.public.comfrontend.cluster.localhttps://loculus.public.com/*Keycloak
(relevant files:
_urls.tpl,ingressroute.yaml,keycloak-deployment.yaml,keycloak-config-map.yaml)The
loculus.keycloakUrltemplate feeds intoKC_HOSTNAME_URL,KC_HOSTNAME_ADMIN_URL, and the realmfrontendUrl. These control the hostname that Keycloak uses for its own external-facing URLs (issuer, token endpoint, etc.).publicHosts.keycloakUrlingressHosts.keycloakKC_HOSTNAME_URL/frontendUrlhttps://authentication<sep><host>authentication<sep><host>auth.cluster.localhttps://auth.cluster.localauth.cluster.localhttps://auth.public.comhttps://auth.public.comauthentication<sep><host>https://auth.public.comauth.cluster.localhttps://auth.public.comauth.cluster.localLAPIS
(relevant files:
_common-metadata.tpl,lapis-ingress.yaml)publicHosts.lapisUrlTemplateingressHosts.lapislapisUrlshttps://lapis<sep><host>/%organism%lapis<sep><host>lapis.cluster.localhttps://lapis.cluster.local/%organism%lapis.cluster.localhttps://lapis.public.com/%organism%https://lapis.public.com/%organism%lapis<sep><host>https://lapis.public.com/%organism%lapis.cluster.localhttps://lapis.public.com/%organism%lapis.cluster.localS3 / MinIO
(relevant files:
_urls.tpl,ingressroute.yaml. (only applies whenrunDevelopmentS3: true))ingressHosts.minios3Urls3UrlInternalhttps://s3<sep><host>http://loculus-minio-service:8084s3<sep><host>s3.cluster.localhttps://s3.cluster.localhttp://loculus-minio-service:8084s3.cluster.localDocs
(relevant files:
docs-preview.yaml. (only applies whenpreviewDocs: true))ingressHosts.docsdocs<sep><host>docs.cluster.localdocs.cluster.localFull example of proposed structure:
networkingis just a suggestion of course, if please feel free to suggest another key name if necessary.all URL and host references in templates (such as
_urls.tpl,ingressroute.yaml,lapis-ingress.yaml, etc.) have been changed to use the new.Values.networkingstructureI figured to not move the keys
hostandlocalHostas i believe these are used too broadly throughout the chart, also outside of networking, or are used only in a development setting.Please let me know your opinions on this, happy to make adjustments or to explore a different approach if this is not in line with your roadmap :)
🚀 Preview: Add
previewlabel to enable