Exposing Spaces externally
You can expose Spaces externally using one of three options:
| Option | When to use |
|---|---|
| LoadBalancer Service | Simplest setup, recommended for most deployments |
| Gateway API | Organization already using Gateway API, or need shared gateway across services |
| Ingress | Organization already using Ingress, or need shared load balancer across services |
LoadBalancer Service
Upbound recommends a LoadBalancer Service to expose spaces-router.
Use a Network Load Balancer (L4), not an Application Load Balancer (L7). Spaces uses long-lived connections for watch traffic that L7 load balancers may timeout.
Configuration
externalTLS:
host: proxy.example.com # Externally routable hostname for TLS certificates
router:
proxy:
service:
type: LoadBalancer
annotations:
# AWS NLB (see Cloud-Specific Annotations for other clouds)
service.beta.kubernetes.io/aws-load-balancer-type: external
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
See Cloud-Specific Annotations for GCP and Azure.
Get the LoadBalancer address
After installation:
kubectl get svc -n upbound-system spaces-router \
-o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
Create or update a DNS record pointing your externalTLS.host to this address.
Ingress
Use Ingress if you need to share a load balancer across multiple services or have specific networking requirements.
Requirements
- TLS passthrough support in your Ingress controller
- Network Load Balancer (L4) strongly recommended for long-lived connections
Configure your Ingress controller's Service with NLB annotations.
Configuration
ingress:
provision: true
host: proxy.example.com
ingressClassName: "<your-ingress-class>"
# Annotations to add to the Ingress resource
annotations: {}
# Pod labels of the Ingress controller - used for network policy
podLabels: {}
# Namespace labels of the Ingress controller - used for network policy
namespaceLabels: {}
Traefik (with nginx provider)
Traefik can use the kubernetesIngressNGINX provider to handle nginx-style Ingress resources with TLS passthrough.
ingress:
provision: true
host: proxy.example.com
ingressClassName: nginx
annotations:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
podLabels:
app.kubernetes.io/name: traefik
namespaceLabels:
kubernetes.io/metadata.name: traefik
Gateway API
Spaces supports the Gateway API. Use this option if your organization is already using Gateway API or needs a shared gateway across multiple services.
Requirements
- A Gateway API controller (for example, Envoy Gateway, Cilium, or Traefik)
- Gateway API CRDs installed in your cluster
- TLS passthrough support
- Network Load Balancer (L4) strongly recommended
For step-by-step setup including installing a controller (such as Envoy Gateway) and creating the GatewayClass, see Migrate away from ingress-nginx - Gateway API.
Gateway API configuration
Add the following to your Helm values (or use --set when upgrading). Disable Ingress when using Gateway API.
ingress:
provision: false
gatewayAPI:
host: proxy.example.com # Externally routable hostname; must match your DNS
gateway:
provision: true
name: spaces
className: spaces # Must match your GatewayClass name
spacesRouterRoute:
provision: true
podLabels:
app.kubernetes.io/name: envoy
app.kubernetes.io/component: proxy
app.kubernetes.io/managed-by: envoy-gateway
namespaceLabels:
kubernetes.io/metadata.name: envoy-gateway-system
The podLabels and namespaceLabels must match your Gateway API controller's pods and namespace (for NetworkPolicy). The example above uses Envoy Gateway; adjust for your controller.
Install or upgrade Spaces with these values:
helm upgrade spaces oci://xpkg.upbound.io/spaces-artifacts/spaces \
--namespace upbound-system \
-f values.yaml \
--wait
Get the Gateway address
After the Gateway is configured by your controller:
kubectl get gateway -n upbound-system spaces \
-o jsonpath='{.status.addresses[0].value}'
Create or update a DNS record so that gatewayAPI.host points to this address.
Troubleshooting
- Gateway status:
kubectl get gateway -n upbound-system spaces -o yaml— look forAcceptedandProgrammedinstatus.conditions. - TLSRoute status:
kubectl get tlsroute -n upbound-system spaces-router -o yaml— route should showAccepted: True. - Connectivity:
curl -k "https://<your-host>/version"— expected response is401 Unauthorized(routing works, auth required).
Cloud-specific annotations
Network Load Balancers (L4) are strongly recommended. Spaces uses long-lived watch connections (hours or days) for kubectl and ArgoCD. L7 load balancers may timeout these connections. Use these annotations on the LoadBalancer Service (spaces-router, Ingress controller, or Gateway).
| Cloud | Annotations |
|---|---|
| AWS | service.beta.kubernetes.io/aws-load-balancer-type: externalservice.beta.kubernetes.io/aws-load-balancer-scheme: internet-facingservice.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip |
| GCP | cloud.google.com/l4-rbs: enabled |
| Azure | None required (L4 by default) |