diff --git a/docs/manuals/spaces/howtos/self-hosted/ingress-nginx-migration.md b/docs/manuals/spaces/howtos/self-hosted/ingress-nginx-migration.md
new file mode 100644
index 000000000..389446b68
--- /dev/null
+++ b/docs/manuals/spaces/howtos/self-hosted/ingress-nginx-migration.md
@@ -0,0 +1,253 @@
+---
+title: Migrate to Envoy Gateway
+sidebar_position: 6
+description: A guide on how to migrate to Envoy Gateway from ingress-nginx
+tier: "business"
+---
+import GlobalLanguageSelector, { CodeBlock } from '@site/src/components/GlobalLanguageSelector';
+
+
+
+
+Upbound recommends migrating from the Ingress API to the Gateway API for routing
+traffic to control planes. This changes addresses critical security issues and
+aligns with the Kuberenetes community decision to retire ingress-nginx in March
+2026.
+
+
+## How?
+
+To migrate from ingress-nginx to Envoy Gateway, you must delete your current
+ingress resource and controller and install the Gateway API implementation with
+TLS passthrough enabled.
+
+
+### 1. Remove existing Ingress resources
+
+Delete the Ingress resource and ingress-nginx controller:
+
+```bash
+kubectl -n upbound-system delete ingress mxe-router-ingress
+helm -n ingress-nginx delete ingress-nginx
+```
+
+:::warning
+This step forces downtime for API access through spaces-router until the
+Gateway API configuration is complete.
+:::
+
+### 2. Install a Gateway API controller
+
+Install a Gateway API implementation that supports TLS passthrough and `TLSRoute`.
+The following example uses Envoy Gateway:
+
+```bash
+export ENVOY_GATEWAY_VERSION=v1.2.4
+
+helm -n envoy-gateway-system upgrade --install --wait --wait-for-jobs \
+ --timeout 300s --create-namespace envoy-gateway \
+ oci://docker.io/envoyproxy/gateway-helm \
+ --version "${ENVOY_GATEWAY_VERSION}"
+```
+
+### 3. Create GatewayClass resource
+
+Create a `GatewayClass` resource appropriate for your cloud provider.
+
+
+
+```bash
+kubectl apply -f - --server-side <
+
+
+
+
+```bash
+kubectl apply -f - --server-side <
+
+
+
+```bash
+kubectl apply -f - --server-side <
+
+
+### 4. Create Gateway resource
+
+Create a Gateway resource in the `upbound-system` namespace.
+
+
+
+```bash
+kubectl apply -f - --server-side <
+
+
+
+
+```bash
+kubectl apply -f - --server-side <
+
+
+
+
+
+
+```bash
+kubectl apply -f - --server-side <
+
+
+
+:::note
+During installation or upgrade, you can use the Spaces Helm chart to create the
+Gateway automatically with these parameters:
+- `gatewayAPI.gateway.provision=true`
+- `gatewayAPI.gateway.className=spaces`
+:::
+
+
+### 5. Get the load balancer hostname
+
+Check the externally routable hostname for the Gateway's load balancer.
+The Helm `gatewayAPI.host` parameter requires this hostname.
+
+For Envoy Gateway, inspect the LoadBalancer service:
+
+```bash
+kubectl get service -n envoy-gateway-system \
+ -l gateway.envoyproxy.io/owning-gateway-name=spaces \
+ -o jsonpath='{.items[0].status.loadBalancer.ingress[0].hostname}'
+```
+
+### 6. Upgrade the Spaces Helm release
+
+Upgrade the Spaces installation with Gateway API parameters:
+
+```bash
+helm -n upbound-system upgrade spaces \
+ oci://xpkg.upbound.io/spaces-artifacts/spaces \
+ --version "${SPACES_VERSION}" \
+ --set "ingress.provision=false" \
+ --set "gatewayAPI.host=${GATEWAY_HOSTNAME}" \
+ --set "account=${UPBOUND_ACCOUNT}" \
+ --reuse-values \
+ --wait
+```
+
+### 7. Restart spaces-router (Optional)
+
+If the `gatewayAPI.host` value differs from the previous `ingress.host` value,
+restart the spaces-router pod to regenerate the certificate with the correct SAN
+(Subject Alternative Name):
+
+```bash
+kubectl -n upbound-system rollout restart deployment spaces-router
+kubectl -n upbound-system rollout status deployment spaces-router
+```
+
+## Additional resources
+
+- [Spaces Deployment]
+- [Kubernetes Announcement]
+
+[spaces deployment]:
+/manuals/spaces/howtos/self-hosted/self-hosted-spaces-deployment
+[kubernetes announcement]: https://www.kubernetes.dev/blog/2025/11/12/ingress-nginx-retirement/
diff --git a/docs/manuals/spaces/howtos/self-hosted/self-hosted-spaces-deployment.md b/docs/manuals/spaces/howtos/self-hosted/self-hosted-spaces-deployment.md
index e549e3939..230aa2411 100644
--- a/docs/manuals/spaces/howtos/self-hosted/self-hosted-spaces-deployment.md
+++ b/docs/manuals/spaces/howtos/self-hosted/self-hosted-spaces-deployment.md
@@ -1,7 +1,7 @@
---
title: Deployment Workflow
sidebar_position: 3
-description: A quickstart guide for Upbound Spaces
+description: A quickstart guide for Upbound Spaces
tier: "business"
---
import GlobalLanguageSelector, { CodeBlock } from '@site/src/components/GlobalLanguageSelector';
@@ -249,59 +249,135 @@ helm install aws-load-balancer-controller aws-load-balancer-controller --namespa
-### Install ingress-nginx
+### Install Envoy Gateway
-Starting with Spaces v1.10.0, you need to configure the ingress-nginx
-controller to allow SSL-passthrough mode. You can do so by passing the
-`--enable-ssl-passthrough=true` command-line option to the controller.
-The following Helm install command enables this with the `controller.extraArgs`
-parameter:
+Starting with Spaces v1.10.0, Upbound recommends using the [Gateway API] for
+routing traffic to Spaces. Gateway API is the official Kubernetes standard for
+ingress and replaces the legacy Ingress API.
+
+This guide uses Envoy Gateway as the Gateway API controller and replaces
+ingress-nginx previously recommended.
+
+:::info
+If you need to continue to use ingress-nginx temporarily, use the [ingress-nginx
+migration guide][migration guide].
+
+The Kubernetes community announced that ingress-nginx will be retired in March
+2026 and you should plan to migrate to Gateway API before then.
+:::
+
+
+First, install Envoy Gateway with Helm:
+
+```bash
+helm -n envoy-gateway-system upgrade --install --wait --wait-for-jobs \
+ --timeout 360s --create-namespace envoy-gateway \
+ oci://docker.io/envoyproxy/gateway-helm \
+ --version "v1.2.4"
+```
+
+Next, create the Gateway API resources for your cloud provider:
+Create EnvoyProxy configuration for AWS load balancer
+
```bash
-helm upgrade --install ingress-nginx ingress-nginx \
- --create-namespace --namespace ingress-nginx \
- --repo https://kubernetes.github.io/ingress-nginx \
- --version 4.12.1 \
- --set 'controller.service.type=LoadBalancer' \
- --set 'controller.extraArgs.enable-ssl-passthrough=true' \
- --set 'controller.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-type=external' \
- --set 'controller.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-scheme=internet-facing' \
- --set 'controller.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-nlb-target-type=ip' \
- --set 'controller.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-healthcheck-protocol=http' \
- --set 'controller.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-healthcheck-path=/healthz' \
- --set 'controller.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-healthcheck-port=10254' \
- --wait
+kubectl apply -f - --server-side <
+```bash
+Create EnvoyProxy configuration for Azure load balancer:
```bash
-helm upgrade --install ingress-nginx ingress-nginx \
- --create-namespace --namespace ingress-nginx \
- --repo https://kubernetes.github.io/ingress-nginx \
- --version 4.12.1 \
- --set 'controller.service.type=LoadBalancer' \
- --set 'controller.extraArgs.enable-ssl-passthrough=true' \
- --set 'controller.service.annotations.service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path=/healthz' \
- --wait
+kubectl apply -f - --server-side <
+Create GatewayClass:
```bash
-helm upgrade --install ingress-nginx ingress-nginx \
- --create-namespace --namespace ingress-nginx \
- --repo https://kubernetes.github.io/ingress-nginx \
- --version 4.12.1 \
- --set 'controller.service.type=LoadBalancer' \
- --set 'controller.extraArgs.enable-ssl-passthrough=true' \
- --wait
+kubectl apply -f - --server-side <
@@ -375,7 +451,10 @@ kubectl get ingress \
-If the preceding command doesn't return a load balancer address then your provider may not have allocated it yet. Once it's available, add a DNS record for the `ROUTER_HOST` to point to the given load balancer address. If it's an IPv4 address, add an A record. If it's a domain name, add a CNAME record.
+If the preceding command doesn't return a load balancer address then your
+provider may not have allocated it yet. Once it's available, add a DNS record
+for the `ROUTER_HOST` to point to the given load balancer address. If it's an
+IPv4 address, add an A record. If it's a domain name, add a CNAME record.
## Configure the up CLI