Skip to content

Clarify confusing router vhost "default" vs "domains" config semantics #3321

Description

@renuka-fernando

Description

The router.vhosts.main / router.vhosts.sandbox configuration (in gateway-helm-chart values → rendered into the controller's config.toml) has two fields — default and domains — whose behavior is very hard to reason about. The field names imply one thing but the code does another. This configuration surface was introduced in #403.

The core confusion: default reads like "the fallback hostname the vhost matches", but in the code it is really a bucket identifier (a routing key), and it is only matched as a Host when domains is empty. The moment domains is set, default is no longer matched against any request — domains becomes the sole Host-match list, and default silently degrades to a label.

Example config:

vhosts:
  main:
    domains: ["api.example.com"]
    default: "foo.com"
  sandbox:
    domains: ["sandbox.api.example.com"]
    default: "sandbox-foo.com"

With this, foo.com / sandbox-foo.com never match any request. Only api.example.com → main upstream and sandbox.api.example.com → sandbox upstream work. An operator who set default: "foo.com" will reasonably expect foo.com to be reachable, and it is not.

Second source of confusion — what is stored vs. what is live:

  • The per-API vhost name is resolved to a concrete value at deploy time (resolveVhostSentinels, gateway/gateway-controller/pkg/utils/api_deployment.go:308) and frozen into the DB (marshaled from SourceConfiguration).
  • domains is not stored per-API — it is read live from config.toml at xDS translation time (getVHostDomains, gateway/gateway-controller/pkg/xds/translator.go:974-1013).

As a result, changing domains in config.toml behaves inconsistently depending on whether default is also changed:

  • Change only domains (keep default): existing APIs are silently re-hosted to the new domains after a restart.
  • Change default: existing APIs stay pinned to their old frozen name and the new domains is ignored for them — it only applies to APIs deployed after the change.

This "freeze" is intentional, but it is undocumented and surprising, and the default/domains naming makes it nearly impossible to predict the Envoy VirtualHost.domains that will actually be produced.

Affected areas

  • kubernetes/helm/gateway-helm-chart/values.yaml (+ templates/gateway/gateway-config.yaml:211-221)
  • gateway/gateway-controller/pkg/config/config.go (VHostEntry, validateVHostsConfig)
  • gateway/gateway-controller/pkg/xds/translator.go (getVHostDomains)
  • gateway/gateway-controller/pkg/utils/api_deployment.go (resolveVhostSentinels)

Suggested improvements (pick/refine)

  • Rename fields to reflect their real roles, e.g. defaultid/name (the bucket identity) and keep domains as the Host-match list; or collapse to a single clearer model.
  • If both are kept, document explicitly that domains overrides default for Host matching, and that default is only matched when domains is empty.
  • Document the deploy-time freeze: the vhost name is persisted per-API, so changing default does not re-route existing APIs, while changing domains (with an unchanged default) does.
  • Consider a validation/warn when an API's explicit vhost equals one of the configured domains (produces a duplicate-domain Envoy config that is rejected).
  • Add operator-facing docs with a worked example mapping config → resulting Envoy VirtualHost.domains.

Version

No response

Related Issue

Introduced by #403

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area/GatewayAny issues related to API deployment in gateway, routing etc.Aspect/ConfigurationConfig files, settings, env vars, defaultsType/Improvement

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions