Routing Modes
Diverge relies on advanced routing techniques to ensure preview environments behave as expected without requiring complete isolation.
Header-Based Routing
Section titled “Header-Based Routing”The primary routing mode in Diverge. When a preview environment is created, a unique header is generated (e.g., x-diverge-env: pr-123).
- Any incoming request with this header is intercepted by your Gateway (Istio/Gateway API).
- The Gateway routes the traffic to your delta deployments.
- If a service in the delta deployment calls another service that wasn’t modified, the header propagates, but the traffic falls back to the baseline service.
This enables seamless integration testing while maximizing resource efficiency.
Istio Ambient & Waypoint Proxies
Section titled “Istio Ambient & Waypoint Proxies”Diverge is fully compatible with Istio Ambient mode (zero sidecars):
- L4 via ztunnel: Secure L4 networking is handled transparently by the node-level ztunnel.
- L7 via Waypoint: Layer 7 routing (
HTTPRoute) for header-based routing is processed by the Waypoint proxy. - Gateway API: Diverge natively generates Kubernetes Gateway API
HTTPRouteandGRPCRouteresources to perform header-based routing, which Istio Ambient consumes. - Convenience Annotations: Set
divergedev.com/ambient-mesh: "true"anddivergedev.com/waypoint: <waypoint-name>on anEnvironmentwithspec.deploy.namespace: createto automatically applyistio.io/dataplane-mode: ambientandistio.io/use-waypoint: <waypoint-name>to the created preview namespace. - Knative Kourier: Fully compatible with Knative’s Kourier ingress for namespaces running serverless scale-to-zero workloads.
Prerequisites
Section titled “Prerequisites”To use L7 header-based routing with Istio Ambient:
- Waypoint Proxy: Deploy an Istio Waypoint proxy in the target namespace (e.g.
istioctl waypoint apply --namespace <namespace>). - Waypoint Enrollment: Ensure the namespace or target workloads are labeled with
istio.io/use-waypoint: <waypoint-name>(or annotate theEnvironmentwithdivergedev.com/waypoint: <waypoint-name>) so L7 traffic is redirected to the Waypoint proxy for policy and routing enforcement. - Ingress Gateway: For external traffic entering the cluster, configure an Ingress Gateway (such as Istio Ingress Gateway or a Gateway API Gateway) with appropriate listeners and routing to forward requests to the Waypoint proxy or services. Use
divergedev.com/gateway-namespace: <ns>when the Gateway lives in a separate namespace from your baseline services.
Cross-Namespace ReferenceGrant, GAMMA East-West Routing & parentRef Disambiguation
Section titled “Cross-Namespace ReferenceGrant, GAMMA East-West Routing & parentRef Disambiguation”- Automatic Cross-Namespace
ReferenceGrant(spec.deploy.namespace: create): When anEnvironmentdeploys its preview workload into an isolated namespace (env.PreviewNamespace()) that differs from the route namespace,GatewayRouterautomatically reconciles agateway.networking.k8s.io/v1beta1ReferenceGrant(diverge-<env>-grant) inenv.PreviewNamespace()granting route-namespaceHTTPRouteandGRPCRouteaccess to each changed previewService(<env>-<service>). - GAMMA East-West Service Mesh Routing (
--enable-gamma): Pass--enable-gammatodiverge-controllersoPreviewGroupReconcilerconfigures childEnvironments’spec.serviceConfig.serviceNamewith the baseline service name;GatewayRoutercreates the GAMMA meshHTTPRoutefromspec.serviceConfig.serviceName(whileEnvironmentReconcilerdoes not take--enable-gammaand usesspec.serviceConfig.serviceNamewhen set directly on theEnvironment). - Gateway API
parentRefDisambiguation (Gatewayvs.Service): North-south ingress routes attach to aGateway(group: gateway.networking.k8s.io,kind: Gateway), while east-west GAMMA routes attach to a KubernetesService(group: "",kind: Service). So services namedpayment-gatewayor Gateways namedprod-ingresswork seamlessly without ambiguous substring heuristics,GatewayRouterresolvesparentRefkind in the following precedence order:- Explicit
Kind/NamePrefix (Gateway/<name>orService/<name>): Setspec.serviceConfig.parentReftoGateway/prod-ingressorService/payment-gateway. - Explicit Annotation (
divergedev.com/parent-kind: Gateway | Service): Annotate theEnvironmentwithdivergedev.com/parent-kind: "Gateway"ordivergedev.com/parent-kind: "Service". - Automatic Cluster
GatewayLookup: Queries the cluster for agateway.networking.k8s.io/v1GatewaymatchingparentRefin the target gateway namespace (divergedev.com/gateway-namespaceor the route namespace) before falling back to heuristic name matching (gateway/waypoint).
- Explicit
Subdomain Routing
Section titled “Subdomain Routing”While header-based routing is optimal for backend microservices and automated API tests, frontend single-page applications (React, Next.js, Vue), mobile web views, and third-party webhooks cannot easily inject custom HTTP headers into browser navigation requests.
Subdomain routing provides clean, browser-accessible URLs for each preview environment by routing traffic based on the HTTP Host header.
How It Works
Section titled “How It Works”- When an environment
pr-123is created, Diverge generates a Gateway APIHTTPRoutematching hostnames likepr-123.preview.example.com. - Traffic sent to
https://pr-123.preview.example.comis intercepted by your Gateway Ingress. - The Gateway routes requests directly to the preview service delta deployment.
- Baseline services remain shared, and internal service calls propagate context seamlessly.
Configuration
Section titled “Configuration”Enable subdomain routing by setting mode: subdomain and defining the target domain in your configuration:
# Environment CRD or .diverge.yamlapiVersion: divergedev.com/v1alpha1kind: Environmentmetadata: name: pr-123 annotations: divergedev.com/external-dns: "true" divergedev.com/cluster-issuer: "letsencrypt-prod"spec: routing: mode: subdomain baseDomain: preview.example.comAutomatic external-dns & cert-manager Integration
Section titled “Automatic external-dns & cert-manager Integration”When mode: subdomain is enabled, GatewayRouter automatically manages DNS annotations on generated HTTPRoute / GRPCRoute resources and TLS configuration on the target Gateway:
external-dns(Gateway API route sources):- Configure ExternalDNS with the Gateway API route sources enabled (
--source=gateway-httprouteand/or--source=gateway-grpcroute). divergedev.com/external-dns: "true"setsexternal-dns.alpha.kubernetes.io/hostname: <env>.<baseDomain>on the generatedHTTPRoute/GRPCRoute(alongside the route’sspec.hostnamesentry<env>.<baseDomain>).divergedev.com/external-dns-hostname: "<hostname>"sets an explicit custom hostname override (external-dns.alpha.kubernetes.io/hostname: <hostname>) and appends<hostname>to the route’sspec.hostnames.divergedev.com/external-dns-ttl: "60"setsexternal-dns.alpha.kubernetes.io/ttl: "60"on the generated route.- Note on ExternalDNS versions: ExternalDNS reads
spec.hostnamesnatively onHTTPRoute/GRPCRoute; ExternalDNS< v0.20readsexternal-dns.alpha.kubernetes.io/*annotations by default, while ExternalDNSv0.20+reads them when--annotation-prefix=external-dns.alpha.kubernetes.io/is enabled.
- Configure ExternalDNS with the Gateway API route sources enabled (
cert-manager(Gateway TLS &Certificateprovisioning):- Because
cert-manager’s Gateway API integration watchesGatewaylisteners rather thancert-manager.io/*annotations onHTTPRouteorGRPCRoute, Diverge does not placecert-manager.io/*annotations on routes. divergedev.com/cluster-issuer: "letsencrypt-prod"(or namespace-scopeddivergedev.com/issuer: "<issuer-name>") reconciles acert-manager.io/v1Certificateresource (<env>-tls) in the Gateway namespace, and propagates the correspondingcert-manager.io/cluster-issuerorcert-manager.io/issuerannotation onto the targetGatewaywhen theGatewayis dedicated to that environment (divergedev.com/environment: <env>anddivergedev.com/managed-by: diverge). Shared or platform-ownedGatewayresources are not mutated by individual preview environments.- Before using this flow, install
cert-managerwith Gateway API integration enabled for the installed version, ensure the Gateway API CRDs are installed beforecert-managerstarts (or restartcert-managerafter installing them), and create the referencedIssuerin theGateway’s namespace or the referenced cluster-scopedClusterIssuer.
- Because
DNS & TLS Requirements
Section titled “DNS & TLS Requirements”To support dynamic subdomains without manual DNS or certificate management:
- Wildcard DNS or
external-dns: Configure a wildcard DNS record (e.g.,*.preview.example.com) pointing to your Ingress Gateway’s external IP/CNAME, or annotate theEnvironmentwithdivergedev.com/external-dns: "true"(with ExternalDNS running--source=gateway-httproute/--source=gateway-grpcroute). - Wildcard TLS Certificate or
cert-manager: Provision a wildcard certificate for*.preview.example.com, or annotate theEnvironmentwithdivergedev.com/cluster-issuer: "letsencrypt-prod"(ordivergedev.com/issuer) so Diverge reconciles acert-manager.io/v1Certificate(<env>-tls) (and updates dedicated environmentGatewayannotations).
Routing Mode Comparison
Section titled “Routing Mode Comparison”| Feature / Aspect | Header-Based Routing | Subdomain Routing | Namespace Routing |
|---|---|---|---|
| Routing Mechanism | L7 HTTP Header (x-diverge-env) |
Host / SNI (<env>.preview.domain) |
Dedicated K8s Namespace |
| Frontend / Browser Support | Requires browser extension or proxy injection | Native browser support (clickable URLs) | Native browser support |
| DNS / TLS Setup | Single static domain & cert | Wildcard DNS (*.domain) & wildcard TLS |
Wildcard or per-namespace DNS/TLS |
| Third-Party Webhooks | Challenging (webhooks lack custom headers) | Native (dedicated callback URL per PR) | Native (dedicated callback URL per PR) |
| Service Sharing (Deltas) | Shared baseline with delta overrides | Shared baseline with delta overrides | Full duplication or cross-namespace mesh |
| Resource Efficiency | Highest (only changed pods deploy) | Highest (only changed pods deploy) | Lower (deploys full stack per env) |
Diverge Proxy & Activator Proxy
Section titled “Diverge Proxy & Activator Proxy”Diverge includes purpose-built proxies to enhance L7 routing, cold-start wake-up, and security:
- Health Endpoints: Uses
/-/healthzand/-/readyz(avoiding/healthzto prevent upstream path shadowing). The readiness probe checks Kubernetes informer cache sync. - Hide Environment List (
--hide-environment-list): On public or shared preview domains, pass--hide-environment-listtodiverge-proxy(trueby default in hardened deployments or configurable via proxy args) so unknown preview hostnames return a generic404 Environment not foundresponse without enumerating active environment names. - Wake-Up Splash Page (
--wake-splash): Onactivator-proxy,--wake-splash(trueby default) returns a202 Acceptedauto-refreshing HTML splash screen (X-Diverge-Wake-Splash: true) and readiness endpoint (GET /-/wakeup-status?env=<env>) when browsers access a scaled-to-zero preview environment. - Graceful Shutdown: Handles
SIGTERMwith a 10-second drain. - Structured Logging: Uses logr/zap for consistent and structured logs.
- CORS: The CORS origin allowlist strictly validates against the preview domain.
