From 8eb3d8f6d5ee9d9e283416ec06ff7c2b1b56f162 Mon Sep 17 00:00:00 2001 From: Paul Abel Date: Fri, 18 Sep 2026 13:07:27 +0100 Subject: [PATCH 1/4] feat: Allow VirtualServerRoute .spec.host to be optional This allows support for attaching VirtualServerRoutes to any VirtualServer that has a matching `route:` or `routeSelector:` labels --- .../virtualserver-and-virtualserverroute-resources.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md b/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md index d89b5452f..6ccb396a3 100644 --- a/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md +++ b/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md @@ -354,11 +354,11 @@ spec: {{}} -Note that each subroute must have a `path` that starts with the same prefix (here `/coffee`), which is defined in the route of the VirtualServer. Additionally, the `host` in the VirtualServerRoute must be the same as the `host` of the VirtualServer. +Note that each subroute must have a `path` that starts with the same prefix (here `/coffee`), which is defined in the route of the VirtualServer. Additionally, when set, the `host` in the VirtualServerRoute must be the same as the `host` of the VirtualServer. |Field | Description | Type | Required | | ---| ---| ---| --- | -|``host`` | The host (domain name) of the server. Must be a valid subdomain as defined in RFC 1123, such as ``my-app`` or ``hello.example.com``. When using a wildcard domain like ``*.example.com`` the domain must be contained in double quotes. Must be the same as the ``host`` of the VirtualServer that references this resource. | ``string`` | Yes | +|``host`` | The host (domain name) of the server. Must be a valid subdomain as defined in RFC 1123, such as ``my-app`` or ``hello.example.com``. When using a wildcard domain like ``*.example.com`` the domain must be contained in double quotes. When set, must be the same as the host of the VirtualServer that references this resource. When omitted (hostless mode), the VirtualServerRoute can be referenced by any VirtualServer regardless of host, enabling the same route configuration to be shared across multiple VirtualServers. | ``string`` | No | |``upstreams`` | A list of upstreams. | [[]upstream](#upstream) | No | |``subroutes`` | A list of subroutes. | [[]subroute](#virtualserverroutesubroute) | No | |``ingressClassName`` | Specifies which Ingress Controller must handle the VirtualServerRoute resource. Must be the same as the ``ingressClassName`` of the VirtualServer that references this resource. | ``string``_ | No | From 50411469c6182af2145672423b0679a74f1afa31 Mon Sep 17 00:00:00 2001 From: Paul Abel Date: Fri, 18 Sep 2026 13:25:48 +0100 Subject: [PATCH 2/4] chore: update grammer --- .../virtualserver-and-virtualserverroute-resources.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md b/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md index 6ccb396a3..8d68d71a2 100644 --- a/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md +++ b/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md @@ -354,11 +354,11 @@ spec: {{}} -Note that each subroute must have a `path` that starts with the same prefix (here `/coffee`), which is defined in the route of the VirtualServer. Additionally, when set, the `host` in the VirtualServerRoute must be the same as the `host` of the VirtualServer. +Note that each subroute must have a `path` that starts with the same prefix (here `/coffee`), which is defined in the route of the VirtualServer. Additionally, if the `host` is set in the VirtualServerRoute, it must match the `host` of the VirtualServer exactly. |Field | Description | Type | Required | | ---| ---| ---| --- | -|``host`` | The host (domain name) of the server. Must be a valid subdomain as defined in RFC 1123, such as ``my-app`` or ``hello.example.com``. When using a wildcard domain like ``*.example.com`` the domain must be contained in double quotes. When set, must be the same as the host of the VirtualServer that references this resource. When omitted (hostless mode), the VirtualServerRoute can be referenced by any VirtualServer regardless of host, enabling the same route configuration to be shared across multiple VirtualServers. | ``string`` | No | +|``host`` | The host (domain name) of the server. Must be a valid subdomain as defined in RFC 1123, such as ``my-app`` or ``hello.example.com``. When using a wildcard domain like ``*.example.com``, the domain must be contained in double quotes. When set, it must match the ``host`` of the VirtualServer that references this resource. When omitted (hostless mode), the VirtualServerRoute can be referenced by any VirtualServer regardless of host, enabling the same route configuration to be shared across multiple VirtualServers. | ``string`` | No | |``upstreams`` | A list of upstreams. | [[]upstream](#upstream) | No | |``subroutes`` | A list of subroutes. | [[]subroute](#virtualserverroutesubroute) | No | |``ingressClassName`` | Specifies which Ingress Controller must handle the VirtualServerRoute resource. Must be the same as the ``ingressClassName`` of the VirtualServer that references this resource. | ``string``_ | No | From 08de9073d5d2d1610b60dcb95edc29701644b694 Mon Sep 17 00:00:00 2001 From: Paul Abel Date: Fri, 18 Sep 2026 14:47:08 +0100 Subject: [PATCH 3/4] chore: enhance the NIC docs relating to hostless VSR --- .../host-and-listener-collisions.md | 7 +- .../cross-namespace-configuration.md | 4 +- content/nic/configuration/path-matching.md | 2 + ...server-and-virtualserverroute-resources.md | 67 +++++++++++++++++-- .../troubleshoot-virtualserver.md | 21 ++++-- 5 files changed, 88 insertions(+), 13 deletions(-) diff --git a/content/nic/configuration/host-and-listener-collisions.md b/content/nic/configuration/host-and-listener-collisions.md index c95095100..2af0d641c 100644 --- a/content/nic/configuration/host-and-listener-collisions.md +++ b/content/nic/configuration/host-and-listener-collisions.md @@ -65,6 +65,7 @@ If `cafe-virtual-server` was created first, it will win the host `cafe.example.c ```shell kubectl describe vs cafe-virtual-server ``` + ```text ... Status: @@ -81,6 +82,7 @@ Events: ```shell kubectl describe ingress cafe-ingress ``` + ```text Events: Type Reason Age From Message @@ -102,7 +104,9 @@ It is possible to merge configuration for multiple Ingress resources for the sam The [Cross-namespace configuration]({{< ref "/nic/configuration/ingress-resources/cross-namespace-configuration.md">}}) topic has more information. -It is *not* possible to merge the configurations for multiple VirtualServer resources for the same host. However, you can split the VirtualServers into multiple VirtualServerRoute resources, which a single VirtualServer can then reference. See the [corresponding example](https://github.com/nginx/kubernetes-ingress/tree/v{{< nic-version >}}/examples/custom-resources/cross-namespace-configuration) on GitHub. +It is *not* possible to merge the configurations for multiple VirtualServer resources for the same host. However, you can split a VirtualServer into multiple VirtualServerRoute resources, which a single VirtualServer can then reference. + +Additionally, a VirtualServerRoute can omit `spec.host` (hostless mode). Because hostless routes do not configure a host, they do not cause host collisions and can serve multiple VirtualServers with different hosts. See the [corresponding example](https://github.com/nginx/kubernetes-ingress/tree/v{{< nic-version >}}/examples/custom-resources/cross-namespace-configuration) on GitHub. It is *not* possible to merge configuration for multiple TransportServer resources. @@ -159,6 +163,7 @@ In our example, if `tcp-1` was created first, it will win the listener `dns-tcp` ```shell kubectl describe ts tcp-2 ``` + ```text ... Events: diff --git a/content/nic/configuration/ingress-resources/cross-namespace-configuration.md b/content/nic/configuration/ingress-resources/cross-namespace-configuration.md index 30e33af76..1c9fa9436 100644 --- a/content/nic/configuration/ingress-resources/cross-namespace-configuration.md +++ b/content/nic/configuration/ingress-resources/cross-namespace-configuration.md @@ -9,6 +9,6 @@ f5-docs: DOCS-594 This topic explains how to spread Ingress configuration across different namespaces in F5 NGINX Ingress Controller. -You can spread the Ingress configuration for a common host across multiple Ingress resources using Mergeable Ingress resources. Such resources can belong to the *same* or *different* namespaces. This enables easier management when using a large number of paths. See the [Mergeable Ingress Resources](https://github.com/nginx/kubernetes-ingress/tree/v{{< nic-version >}}/examples/ingress-resources/mergeable-ingress-types) example in our GitHub repo. When [`-allow-empty-ingress-host`]({{< ref "/nic/configuration/global-configuration/command-line-arguments.md#cmdoption-allow-empty-ingress-host" >}}) is enabled, this also applies to Ingress resources without a `host`. +You can spread the Ingress configuration for a common host across multiple Ingress resources using Mergeable Ingress resources. Such resources can belong to the *same* or *different* namespaces. This simplifies management when using a large number of paths. See the [Mergeable Ingress Resources](https://github.com/nginx/kubernetes-ingress/tree/v{{< nic-version >}}/examples/ingress-resources/mergeable-ingress-types) example in our GitHub repo. When [`-allow-empty-ingress-host`]({{< ref "/nic/configuration/global-configuration/command-line-arguments.md#cmdoption-allow-empty-ingress-host" >}}) is enabled, this also applies to Ingress resources without a `host`. -As an alternative to Mergeable Ingress resources, you can use [VirtualServer and VirtualServerRoute resources]({{< ref "/nic//configuration/virtualserver-and-virtualserverroute-resources.md" >}}) for cross-namespace configuration. See the [Cross-Namespace Configuration](https://github.com/nginx/kubernetes-ingress/tree/v{{< nic-version >}}/examples/custom-resources/cross-namespace-configuration) example in our GitHub repo. +As an alternative to Mergeable Ingress resources, you can use [VirtualServer and VirtualServerRoute resources]({{< ref "/nic/configuration/virtualserver-and-virtualserverroute-resources.md" >}}) for cross-namespace configuration. When you omit `spec.host` (hostless mode), multiple VirtualServers across different namespaces and domains can reference the same VirtualServerRoute. See the [Cross-Namespace Configuration](https://github.com/nginx/kubernetes-ingress/tree/v{{< nic-version >}}/examples/custom-resources/cross-namespace-configuration) example in our GitHub repo. diff --git a/content/nic/configuration/path-matching.md b/content/nic/configuration/path-matching.md index 094617e9f..b412aa67d 100644 --- a/content/nic/configuration/path-matching.md +++ b/content/nic/configuration/path-matching.md @@ -149,6 +149,8 @@ With this configuration: When a VirtualServer route delegates to a VirtualServerRoute using the `route` or `routeSelector` field, the parent route's path acts as a constraint on subroute paths. For a `^~` parent path, each subroute must also use the `^~` modifier. +Setting `spec.host` in the VirtualServerRoute is optional. The path constraint applies whether the VirtualServerRoute specifies a matching host or omits `spec.host` (hostless mode). + ### VirtualServer with delegation ```yaml diff --git a/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md b/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md index 8d68d71a2..5bd78a379 100644 --- a/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md +++ b/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md @@ -236,7 +236,9 @@ See the [VirtualServerRoute specification](#virtualserverroute-specification) se The VirtualServerRoute resource defines a route for a VirtualServer. It can consist of one or multiple subroutes. The VirtualServerRoute is an alternative to [Mergeable Ingress types]({{< ref "/nic/configuration/ingress-resources/cross-namespace-configuration.md" >}}). -VirtualServer routes can reference VirtualServerRoute resources in two ways: by name using the `route` field, or dynamically using the `routeSelector` field with label selectors. The `routeSelector` approach allows you to add new VirtualServerRoute resources without modifying the VirtualServer configuration. +VirtualServer routes can reference VirtualServerRoute resources in two ways: by name using the `route` field, or dynamically using the `routeSelector` field with label selectors. With `routeSelector`, you can add new VirtualServerRoute resources without changing the VirtualServer configuration. + +A VirtualServerRoute can define a `host` to restrict route attachment to a specific VirtualServer, or omit `host` (hostless mode) so multiple VirtualServers can share the same route configuration. {{}} @@ -352,16 +354,69 @@ spec: {{%/tab%}} +{{%tab name="Hostless route"%}} + +In this example, the VirtualServerRoute `shared-coffee` omits the `host` field (hostless mode). Multiple VirtualServers with different domains can reference the same route configuration. + +VirtualServer: + +```yaml +apiVersion: k8s.nginx.org/v1 +kind: VirtualServer +metadata: + name: cafe + namespace: cafe-ns +spec: + host: cafe.example.com + upstreams: + - name: tea + service: tea-svc + port: 80 + routes: + - path: /tea + action: + pass: tea + - path: /coffee + route: coffee-ns/shared-coffee +``` + +VirtualServerRoute (hostless): + +```yaml +apiVersion: k8s.nginx.org/v1 +kind: VirtualServerRoute +metadata: + name: shared-coffee + namespace: coffee-ns +spec: + upstreams: + - name: latte + service: latte-svc + port: 80 + - name: espresso + service: espresso-svc + port: 80 + subroutes: + - path: /coffee/latte + action: + pass: latte + - path: /coffee/espresso + action: + pass: espresso +``` + +{{%/tab%}} + {{}} -Note that each subroute must have a `path` that starts with the same prefix (here `/coffee`), which is defined in the route of the VirtualServer. Additionally, if the `host` is set in the VirtualServerRoute, it must match the `host` of the VirtualServer exactly. +Each subroute path must start with the prefix defined in the VirtualServer route (for example, `/coffee`). If you set `host` in the VirtualServerRoute, it must match the VirtualServer `host` exactly. If you omit `host`, any VirtualServer can reference the VirtualServerRoute. |Field | Description | Type | Required | | ---| ---| ---| --- | -|``host`` | The host (domain name) of the server. Must be a valid subdomain as defined in RFC 1123, such as ``my-app`` or ``hello.example.com``. When using a wildcard domain like ``*.example.com``, the domain must be contained in double quotes. When set, it must match the ``host`` of the VirtualServer that references this resource. When omitted (hostless mode), the VirtualServerRoute can be referenced by any VirtualServer regardless of host, enabling the same route configuration to be shared across multiple VirtualServers. | ``string`` | No | +|``host`` | The host (domain name) of the server. Must be a valid subdomain as defined in RFC 1123, such as ``my-app`` or ``hello.example.com``. When using a wildcard domain like ``*.example.com``, wrap the domain in double quotes. When set, it must match the ``host`` of the VirtualServer that references this resource. When omitted (hostless mode), any VirtualServer can reference the VirtualServerRoute regardless of host, so multiple VirtualServers can share the same route configuration. | ``string`` | No | |``upstreams`` | A list of upstreams. | [[]upstream](#upstream) | No | |``subroutes`` | A list of subroutes. | [[]subroute](#virtualserverroutesubroute) | No | -|``ingressClassName`` | Specifies which Ingress Controller must handle the VirtualServerRoute resource. Must be the same as the ``ingressClassName`` of the VirtualServer that references this resource. | ``string``_ | No | +|``ingressClassName`` | Specifies which Ingress Controller must handle the VirtualServerRoute resource. Must be the same as the ``ingressClassName`` of the VirtualServer that references this resource. | ``string`` | No | ### VirtualServerRoute.Subroute @@ -1113,9 +1168,9 @@ Status: State: Invalid ``` -NGINX Ingress Controller validates VirtualServerRoute resources in a similar way. +NGINX Ingress Controller validates VirtualServerRoute resources in a similar way. For example, if a VirtualServerRoute defines a `host` that doesn't match the referencing VirtualServer, NGINX Ingress Controller rejects the route attachment. When you omit `host` (hostless mode), any VirtualServer can reference the route without host matching. -**Note**: If you make an existing resource invalid, NGINX Ingress Controller will reject it and remove the corresponding configuration from NGINX. +If you make an existing resource invalid, NGINX Ingress Controller rejects it and removes the corresponding configuration from NGINX. ## Multiple regex routes in a VirtualServerRoute diff --git a/content/nic/troubleshooting/troubleshoot-virtualserver.md b/content/nic/troubleshooting/troubleshoot-virtualserver.md index 2c4ace18f..d3dc60741 100644 --- a/content/nic/troubleshooting/troubleshoot-virtualserver.md +++ b/content/nic/troubleshooting/troubleshoot-virtualserver.md @@ -7,16 +7,17 @@ f5-product: NGINX Ingress Controller f5-content-type: how-to --- -This page describes how to troubleshoot VirtualServer and VirtualServer resource events. +This page describes how to troubleshoot VirtualServer and VirtualServerRoute resource events. ## Inspecting VirtualServer and VirtualServerRoute resource events -After creating or updating a VirtualServer resource, you can immediately check if the NGINX configuration for that resource was successfully by using `kubectl describe vs `: +After creating or updating a VirtualServer resource, you can immediately check if the NGINX configuration for that resource was successful by using `kubectl describe vs `: ```shell kubectl describe vs cafe ``` -```shell + +```text Events: Type Reason Age From Message ---- ------ ---- ---- ------- @@ -30,9 +31,21 @@ Checking the events of a VirtualServerRoute is similar: ```shell kubectl describe vsr coffee ``` -```shell + +```text Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal AddedOrUpdated 1m nginx-ingress-controller Configuration for default/coffee was added or updated ``` + +## Common troubleshooting scenarios + +### Host mismatch rejection + +If a VirtualServerRoute defines a `spec.host` that doesn't match the referencing VirtualServer, NGINX Ingress Controller rejects the route attachment and logs a warning event on the resource. + +To resolve a host mismatch: + +- Update `spec.host` in the VirtualServerRoute to match the VirtualServer `host` exactly. +- Omit `spec.host` from the VirtualServerRoute (hostless mode) so any VirtualServer can reference it. From b4a9bc450092c9b2ee1a6ed98cde276895a5bf36 Mon Sep 17 00:00:00 2001 From: Paul Abel Date: Fri, 18 Sep 2026 15:19:47 +0100 Subject: [PATCH 4/4] chore: address feedback --- .../cross-namespace-configuration.md | 4 +- ...server-and-virtualserverroute-resources.md | 42 +++++++++---------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/content/nic/configuration/ingress-resources/cross-namespace-configuration.md b/content/nic/configuration/ingress-resources/cross-namespace-configuration.md index 1c9fa9436..b0fb483ca 100644 --- a/content/nic/configuration/ingress-resources/cross-namespace-configuration.md +++ b/content/nic/configuration/ingress-resources/cross-namespace-configuration.md @@ -11,4 +11,6 @@ This topic explains how to spread Ingress configuration across different namespa You can spread the Ingress configuration for a common host across multiple Ingress resources using Mergeable Ingress resources. Such resources can belong to the *same* or *different* namespaces. This simplifies management when using a large number of paths. See the [Mergeable Ingress Resources](https://github.com/nginx/kubernetes-ingress/tree/v{{< nic-version >}}/examples/ingress-resources/mergeable-ingress-types) example in our GitHub repo. When [`-allow-empty-ingress-host`]({{< ref "/nic/configuration/global-configuration/command-line-arguments.md#cmdoption-allow-empty-ingress-host" >}}) is enabled, this also applies to Ingress resources without a `host`. -As an alternative to Mergeable Ingress resources, you can use [VirtualServer and VirtualServerRoute resources]({{< ref "/nic/configuration/virtualserver-and-virtualserverroute-resources.md" >}}) for cross-namespace configuration. When you omit `spec.host` (hostless mode), multiple VirtualServers across different namespaces and domains can reference the same VirtualServerRoute. See the [Cross-Namespace Configuration](https://github.com/nginx/kubernetes-ingress/tree/v{{< nic-version >}}/examples/custom-resources/cross-namespace-configuration) example in our GitHub repo. +As an alternative to Mergeable Ingress resources, you can use [VirtualServer and VirtualServerRoute resources]({{< ref "/nic/configuration/virtualserver-and-virtualserverroute-resources.md" >}}) for cross-namespace configuration. See the [Cross-Namespace Configuration](https://github.com/nginx/kubernetes-ingress/tree/v{{< nic-version >}}/examples/custom-resources/cross-namespace-configuration) example in our GitHub repo. + +When you omit `spec.host` (hostless mode), multiple VirtualServers across different namespaces and domains can reference the same VirtualServerRoute. A hostless Minion Ingress requires a Master Ingress with an empty host. In contrast, a hostless VirtualServerRoute attaches to any VirtualServer and only processes traffic for that VirtualServer host. diff --git a/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md b/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md index 5bd78a379..e32994b8a 100644 --- a/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md +++ b/content/nic/configuration/virtualserver-and-virtualserverroute-resources.md @@ -296,9 +296,9 @@ spec: {{%/tab%}} -{{%tab name="RouteSelector"%}} +{{%tab name="Hostless route"%}} -In this example, the VirtualServer `cafe` from the namespace `cafe-ns` uses `routeSelector` to dynamically select any VirtualServerRoute with the label `app: coffee`. +In this example, the VirtualServerRoute `shared-coffee` omits the `host` field (hostless mode). Multiple VirtualServers with different domains can reference the same route configuration. VirtualServer: @@ -318,24 +318,19 @@ spec: - path: /tea action: pass: tea - - path: /decaf - routeSelector: - matchLabels: - app: coffee + - path: /coffee + route: coffee-ns/shared-coffee ``` -VirtualServerRoute: +VirtualServerRoute (hostless): ```yaml apiVersion: k8s.nginx.org/v1 kind: VirtualServerRoute metadata: - name: decaf + name: shared-coffee namespace: coffee-ns - labels: - app: coffee spec: - host: cafe.example.com upstreams: - name: latte service: latte-svc @@ -344,19 +339,19 @@ spec: service: espresso-svc port: 80 subroutes: - - path: /decaf/latte + - path: /coffee/latte action: pass: latte - - path: /decaf/espresso + - path: /coffee/espresso action: pass: espresso ``` {{%/tab%}} -{{%tab name="Hostless route"%}} +{{%tab name="RouteSelector"%}} -In this example, the VirtualServerRoute `shared-coffee` omits the `host` field (hostless mode). Multiple VirtualServers with different domains can reference the same route configuration. +In this example, the VirtualServer `cafe` from the namespace `cafe-ns` uses `routeSelector` to dynamically select any VirtualServerRoute with the label `app: coffee`. VirtualServer: @@ -376,19 +371,24 @@ spec: - path: /tea action: pass: tea - - path: /coffee - route: coffee-ns/shared-coffee + - path: /decaf + routeSelector: + matchLabels: + app: coffee ``` -VirtualServerRoute (hostless): +VirtualServerRoute: ```yaml apiVersion: k8s.nginx.org/v1 kind: VirtualServerRoute metadata: - name: shared-coffee + name: decaf namespace: coffee-ns + labels: + app: coffee spec: + host: cafe.example.com upstreams: - name: latte service: latte-svc @@ -397,10 +397,10 @@ spec: service: espresso-svc port: 80 subroutes: - - path: /coffee/latte + - path: /decaf/latte action: pass: latte - - path: /coffee/espresso + - path: /decaf/espresso action: pass: espresso ```