Gateway API Deployment Guide
Deploy DevOps AI Toolkit MCP server using Kubernetes Gateway API v1 with production-ready reference pattern or development/testing creation pattern.
When to Use Gateway API
Use Gateway API when:
- Running Kubernetes 1.26+ with Gateway API support (GKE Autopilot, EKS, AKS, etc.)
- Need advanced routing (weighted traffic, header-based routing, mirroring)
- Prefer role-oriented design (platform team manages Gateways, app teams create routes)
- Want standardized configuration across different Gateway implementations
Use Ingress instead when:
- Running Kubernetes < 1.26
- Gateway API CRDs not available
- Simpler requirements met by Ingress features
Learn more: Gateway API official documentation
Prerequisites
All deployments:
- Kubernetes 1.26+ cluster
- Gateway API CRDs v1.2+ installed
- Gateway controller running (Istio, Envoy Gateway, Kong, etc.)
- Helm 3.x
Reference pattern (RECOMMENDED):
- Existing Gateway resource created by platform team
- GatewayClass resource available
- Optional: ReferenceGrant for cross-namespace access
Creation pattern (development/testing only):
- GatewayClass resource available
Install Gateway API CRDs
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.1/standard-install.yaml
Output:
customresourcedefinition.apiextensions.k8s.io/gatewayclasses.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/gateways.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/httproutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/referencegrants.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/grpcroutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/tcproutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/udproutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/backendtlspolicies.gateway.networking.k8s.io created
Verify installation:
kubectl get crd gateways.gateway.networking.k8s.io httproutes.gateway.networking.k8s.io
Output:
NAME CREATED AT
gateways.gateway.networking.k8s.io 2025-12-22T09:37:24Z
httproutes.gateway.networking.k8s.io 2025-12-22T09:37:24Z
Check available GatewayClasses:
kubectl get gatewayclass
Output:
NAME CONTROLLER ACCEPTED AGE
kgateway kgateway.dev/kgateway True 22s
Reference Pattern (HTTP) - Development/Testing Only
⚠️ SECURITY WARNING: This HTTP-only configuration transmits all traffic unencrypted, including authentication tokens and API keys. DO NOT use in production. Use the HTTPS pattern for production deployments.
The reference pattern follows Gateway API best practices where platform teams manage shared Gateway infrastructure and application teams create HTTPRoutes.
Step 1: Platform Team Creates Gateway (ONCE)
Create a shared Gateway in a dedicated namespace:
Note: This HTTP-only example is for development/testing. For production, skip to the HTTPS pattern.
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cluster-gateway
namespace: gateway-system
spec:
gatewayClassName: istio # Use your GatewayClass name
listeners:
- name: http
protocol: HTTP
port: 80 # ⚠️ HTTP-only - Use HTTPS (port 443) for production
allowedRoutes:
namespaces:
from: All # Allow routes from all namespaces
Apply and wait for Gateway to be ready:
kubectl create namespace gateway-system
Output:
namespace/gateway-system created
Create the Gateway:
kubectl apply -f - <<EOF
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cluster-gateway
namespace: gateway-system
spec:
gatewayClassName: kgateway
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All # Allow routes from all namespaces
EOF
Output:
gateway.gateway.networking.k8s.io/cluster-gateway created
Wait for Gateway to be Programmed:
kubectl wait --for=condition=Programmed gateway/cluster-gateway -n gateway-system --timeout=300s
Output:
gateway.gateway.networking.k8s.io/cluster-gateway condition met
Verify Gateway status:
kubectl describe gateway cluster-gateway -n gateway-system
Output:
Name: cluster-gateway
Namespace: gateway-system
Labels: <none>
Annotations: <none>
API Version: gateway.networking.k8s.io/v1
Kind: Gateway
Metadata:
Creation Timestamp: 2025-12-22T09:38:15Z
Spec:
Gateway Class Name: kgateway
Listeners:
Allow Dropped Headers:
Forward: false
Allow Dropped Headers:
Forward: false
Allowed Routes:
Namespaces:
From: All
Name: http
Port: 80
Protocol: HTTP
Status:
Addresses:
Type: IPAddress
Value: XXX.XXX.XXX.XXX
Conditions:
Last Transition Time: 2025-12-22T09:38:17Z
Message: Resource is valid
Observed Generation: 1
Reason: Accepted
Status: True
Type: Accepted
Last Transition Time: 2025-12-22T09:38:17Z
Message: Listeners are ready
Observed Generation: 1
Reason: Programmed
Status: True
Type: Programmed
Listeners:
Attached Routes: 0
Conditions:
Last Transition Time: 2025-12-22T09:38:17Z
Message: Listener is ready
Observation Timestamp: 2025-12-22T09:38:17Z
Reason: Ready
Status: True
Type: Ready
Name: http
Supported Kinds:
Group: gateway.networking.k8s.io
Kind: HTTPRoute
Group: gateway.networking.k8s.io
Kind: GRPCRoute
Step 2: Application Team Deploys dot-ai
Create Helm values file (gateway-http-values.yaml):
# Deployment method
deployment:
method: standard
# Disable traditional Ingress
ingress:
enabled: false
# Reference existing Gateway (RECOMMENDED)
gateway:
name: "cluster-gateway"
namespace: "gateway-system"
timeouts:
request: "3600s"
backendRequest: "3600s"
# Secrets configuration
secrets:
name: dot-ai-secrets
auth:
token: "" # SET THIS: openssl rand -base64 32
anthropic:
apiKey: "" # SET THIS: sk-ant-api03-...
openai:
apiKey: "" # SET THIS: sk-proj-...
# AI provider configuration
ai:
provider: anthropic
# Qdrant vector database
qdrant:
enabled: true
Deploy with Helm:
helm install dot-ai \
oci://ghcr.io/vfarcic/dot-ai/charts/dot-ai:0.168.0 \
--namespace dot-ai \
--create-namespace \
-f gateway-http-values.yaml \
--wait
Output:
NAME: dot-ai
LAST DEPLOYED: Sun Dec 22 09:39:45 2025
NAMESPACE: dot-ai
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
DevOps AI Toolkit (dot-ai) has been deployed successfully.
Access the service:
- HTTP: kubectl port-forward -n dot-ai svc/dot-ai 8080:8080
- Gateway API: Configure your DNS to point to the Gateway IP (XXX.XXX.XXX.XXX)
For more information, visit: https://github.com/vfarcic/dot-ai
Or using --set flags:
export DOT_AI_AUTH_TOKEN=$(openssl rand -base64 32)
helm install dot-ai \
oci://ghcr.io/vfarcic/dot-ai/charts/dot-ai:0.168.0 \
--namespace dot-ai \
--create-namespace \
--set gateway.name=cluster-gateway \
--set gateway.namespace=gateway-system \
--set secrets.auth.token="$DOT_AI_AUTH_TOKEN" \
--set secrets.anthropic.apiKey="$ANTHROPIC_API_KEY" \
--set secrets.openai.apiKey="$OPENAI_API_KEY" \
--wait
Output:
NAME: dot-ai
LAST DEPLOYED: Sun Dec 22 09:39:45 2025
NAMESPACE: dot-ai
STATUS: deployed
REVISION: 1
Verify deployment:
kubectl get pods -n dot-ai
Output:
NAME READY STATUS RESTARTS AGE
dot-ai-7c5f8d9b4-xyz9w 1/1 Running 0 30s
qdrant-0 1/1 Running 0 25s
Reference Pattern (HTTPS) - RECOMMENDED
For production HTTPS deployments with cert-manager and wildcard certificates.
Step 1: Platform Team Creates Gateway with HTTPS (ONCE)
Create wildcard certificate and Gateway with HTTPS listener:
---
# Wildcard certificate managed by platform team
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-tls
namespace: gateway-system
spec:
secretName: wildcard-tls
issuerRef:
name: letsencrypt-prod # Use your ClusterIssuer
kind: ClusterIssuer
dnsNames:
- "*.example.com" # Your wildcard domain
- "example.com"
---
# Gateway with HTTP and HTTPS listeners
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cluster-gateway
namespace: gateway-system
spec:
gatewayClassName: istio
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: wildcard-tls
allowedRoutes:
namespaces:
from: All
Apply and wait:
kubectl apply -f - <<EOF
---
# Wildcard certificate managed by platform team
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-tls
namespace: gateway-system
spec:
secretName: wildcard-tls
issuerRef:
name: letsencrypt-prod # Use your ClusterIssuer
kind: ClusterIssuer
dnsNames:
- "*.example.com" # Your wildcard domain
- "example.com"
EOF
kubectl wait --for=condition=Ready certificate/wildcard-tls -n gateway-system --timeout=300s
kubectl apply -f - <<EOF
---
# Gateway with HTTP and HTTPS listeners
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cluster-gateway
namespace: gateway-system
spec:
gatewayClassName: istio
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: wildcard-tls
allowedRoutes:
namespaces:
from: All
EOF
kubectl wait --for=condition=Programmed gateway/cluster-gateway -n gateway-system --timeout=300s
Step 2: Application Team Deploys dot-ai
Same as HTTP deployment - the HTTPRoute will work with both HTTP and HTTPS listeners:
helm install dot-ai \
oci://ghcr.io/vfarcic/dot-ai/charts/dot-ai:0.168.0 \
--namespace dot-ai \
--create-namespace \
--set gateway.name=cluster-gateway \
--set gateway.namespace=gateway-system \
--set secrets.auth.token="$DOT_AI_AUTH_TOKEN" \
--set secrets.anthropic.apiKey="$ANTHROPIC_API_KEY" \
--set secrets.openai.apiKey="$OPENAI_API_KEY" \
--wait
Configuration Reference
Reference Pattern Values
gateway:
name: "cluster-gateway" # Name of existing Gateway (required)
namespace: "gateway-system" # Gateway namespace (optional, omit if same namespace)
timeouts:
request: "3600s" # SSE streaming timeout
backendRequest: "3600s"
Creation Pattern Values (Development/Testing Only)
gateway:
create: true # Create Gateway (NOT for production)
className: "istio" # GatewayClass name (required)
annotations: {} # Gateway annotations
listeners:
http:
enabled: true # HTTP listener port 80
hostname: "" # Optional hostname
https:
enabled: false # HTTPS listener port 443
hostname: "" # Optional hostname
secretName: "" # TLS secret name
timeouts:
request: "3600s"
backendRequest: "3600s"
Cross-Namespace Gateway Access (ReferenceGrant)
If the Gateway uses allowedRoutes.namespaces.from: Same, create a ReferenceGrant to allow cross-namespace access.
When is ReferenceGrant needed?
# NO ReferenceGrant needed
listeners:
- name: http
allowedRoutes:
namespaces:
from: All # Allows all namespaces
# ReferenceGrant REQUIRED
listeners:
- name: http
allowedRoutes:
namespaces:
from: Same # Only same namespace (requires ReferenceGrant for cross-namespace)
ReferenceGrant Example
Platform team creates in Gateway namespace:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-dot-ai-routes
namespace: gateway-system # Gateway namespace
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: dot-ai # Application namespace
to:
- group: gateway.networking.k8s.io
kind: Gateway
# Optional: name: cluster-gateway # Restrict to specific Gateway
Apply:
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-dot-ai-routes
namespace: gateway-system # Gateway namespace
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: dot-ai # Application namespace
to:
- group: gateway.networking.k8s.io
kind: Gateway
# Optional: name: cluster-gateway # Restrict to specific Gateway
EOF
Learn more: Gateway API ReferenceGrant documentation
Development/Testing Pattern (Creation Mode)
⚠️ WARNING: This creates a Gateway per application. NOT RECOMMENDED for production.
Use ONLY for:
- Development environments
- Testing Gateway API functionality
- Single-application clusters
- When platform team doesn't provide shared Gateway
Example: Creation Mode with external-dns
# Deployment method
deployment:
method: standard
# Disable Ingress
ingress:
enabled: false
# Create Gateway (NOT RECOMMENDED for production)
gateway:
create: true
className: "istio"
# external-dns annotations
annotations:
external-dns.alpha.kubernetes.io/hostname: "dot-ai.example.com"
listeners:
http:
enabled: true
hostname: "dot-ai.example.com"
https:
enabled: false
timeouts:
request: "3600s"
backendRequest: "3600s"
# Secrets configuration
secrets:
name: dot-ai-secrets
auth:
token: "" # SET THIS
anthropic:
apiKey: "" # SET THIS
openai:
apiKey: "" # SET THIS
ai:
provider: anthropic
qdrant:
enabled: true
Deploy:
helm install dot-ai \
oci://ghcr.io/vfarcic/dot-ai/charts/dot-ai:0.168.0 \
--namespace dot-ai \
--create-namespace \
--set gateway.create=true \
--set gateway.className=istio \
--set 'gateway.annotations.external-dns\.alpha\.kubernetes\.io/hostname=dot-ai.example.com' \
--set gateway.listeners.http.hostname=dot-ai.example.com \
--set secrets.auth.token="$DOT_AI_AUTH_TOKEN" \
--set secrets.anthropic.apiKey="$ANTHROPIC_API_KEY" \
--set secrets.openai.apiKey="$OPENAI_API_KEY" \
--wait
Note: Created Gateway has -http suffix (e.g., dot-ai-http) to prevent kGateway Envoy deployment naming conflicts.
MCP Client Configuration
Configure your MCP client (e.g., Claude Desktop) to connect to the deployed server.
Get Gateway Address
# Reference mode
kubectl get gateway cluster-gateway -n gateway-system -o jsonpath='{.status.addresses[0].value}'
# Creation mode
kubectl get gateway -n dot-ai -o jsonpath='{.items[0].status.addresses[0].value}'
Configure DNS
Point your hostname to the Gateway IP address:
- Manual DNS: Create A record pointing to Gateway IP
- With external-dns: DNS records created automatically
MCP Client Configuration (.mcp.json)
Production (HTTPS - RECOMMENDED):
{
"mcpServers": {
"dot-ai": {
"url": "https://dot-ai.example.com",
"transport": {
"type": "http"
}
}
}
}
Development/Testing Only (HTTP - Not Secure):
⚠️ WARNING: HTTP transmits credentials in plaintext. Use only for local development.
{
"mcpServers": {
"dot-ai": {
"url": "http://dot-ai.example.com",
"transport": {
"type": "http"
}
}
}
}
Verification Steps
1. Check Gateway Status
# Reference mode
kubectl get gateway cluster-gateway -n gateway-system
Output:
NAME CLASS ADDRESS PROGRAMMED AGE
cluster-gateway kgateway XXX.XXX.XXX.XXX True 5m22s
Verify detailed status:
kubectl describe gateway cluster-gateway -n gateway-system
Look for Programmed condition = True.
2. Check HTTPRoute
kubectl get httproute -n dot-ai
Output:
NAME HOSTNAMES PARENTREFS AGE
dot-ai-http [dot-ai.example.com] cluster-gateway 2m
Verify detailed status:
kubectl describe httproute -n dot-ai
Output (excerpt):
Status:
Parents:
- Conditions:
- Type: Accepted
Status: "True"
Reason: Accepted
- Type: ResolvedRefs
Status: "True"
Reason: ResolvedRefs
Controller Name: kgateway.dev/kgateway
Parent Ref:
Group: gateway.networking.k8s.io
Kind: Gateway
Name: cluster-gateway
Namespace: gateway-system
3. Check Backend Services
kubectl get svc,pod -n dot-ai
Output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/dot-ai ClusterIP XXX.XXX.XXX.XX <none> 8080/TCP 2m
NAME READY STATUS RESTARTS AGE
pod/dot-ai-7c5f8d9b4-xyz9w 1/1 Running 0 2m
pod/qdrant-0 1/1 Running 0 2m
4. Verify Gateway IP Address
# Get Gateway IP
kubectl get gateway cluster-gateway -n gateway-system -o jsonpath='{.status.addresses[0].value}'
Output:
XXX.XXX.XXX.XXX
Troubleshooting
Gateway Not Getting IP Address
kubectl describe gateway cluster-gateway -n gateway-system
Common issues:
- GatewayClass not found → Check
kubectl get gatewayclass - Gateway controller not running → Check controller pods
- Invalid listener configuration → Review Gateway spec
Solution:
# Verify GatewayClass exists and is accepted
kubectl get gatewayclass -o yaml
# Check Gateway controller logs
kubectl logs -n istio-system -l app=istio-ingressgateway # Example for Istio
HTTPRoute Not Routing Traffic
kubectl describe httproute -n dot-ai
Common issues:
- Gateway name mismatch
- Cross-namespace without ReferenceGrant
- Backend Service doesn't exist
Solution:
# Verify parentRef matches Gateway
kubectl get httproute -n dot-ai -o yaml | grep -A 5 parentRefs
# Check Service exists
kubectl get svc -n dot-ai
# For cross-namespace, verify ReferenceGrant
kubectl get referencegrant -n gateway-system
Cross-Namespace Access Denied
kubectl get referencegrant -n gateway-system
kubectl describe referencegrant -n gateway-system
Common issues:
- ReferenceGrant missing
- ReferenceGrant doesn't allow your namespace
- Gateway uses
from: Samewithout ReferenceGrant
Solution: Create ReferenceGrant as shown in Cross-Namespace Gateway Access section.
Gateway Name Conflict (Creation Mode)
kubectl get gateway,deploy -n dot-ai
Issue: Gateway name conflicts with application Deployment name.
Solution: The chart automatically adds -http suffix to prevent this. Verify:
kubectl get gateway -n dot-ai
# Expected: dot-ai-http (or <release-name>-http)
DNS Record Not Created (external-dns)
kubectl logs -n external-dns -l app.kubernetes.io/name=external-dns
Common issues:
- external-dns not running
- Missing annotations on Gateway
- DNS provider credentials missing
Solution:
# Verify Gateway has external-dns annotation
kubectl get gateway -n dot-ai -o yaml | grep external-dns
# Check DNS resolution
dig +short dot-ai.example.com
Connection Timeout
Issue: HTTP requests timeout or SSE connections fail.
Solution: Verify timeout configuration:
kubectl get httproute -n dot-ai -o yaml | grep -A 5 timeouts
Expected:
timeouts:
request: 3600s
backendRequest: 3600s
Cost Comparison
| Mode | Gateway per App | Load Balancer Cost | 10 Apps Total Cost |
|---|---|---|---|
| Reference (RECOMMENDED) | No (shared) | $18-30/month | $18-30/month |
| Creation (dev/test) | Yes | $18-30/month each | $180-300/month |
Recommendation: Use reference pattern for production to save costs and follow Gateway API best practices.
Migration from Ingress
Migrate from traditional Ingress to Gateway API:
Step 1: Note Current Configuration
# Get current Ingress hostname
kubectl get ingress -n dot-ai -o yaml | grep host
# Get TLS configuration
kubectl get ingress -n dot-ai -o yaml | grep -A 5 tls
Step 2: Platform Team Creates Gateway
Create Gateway with same hostname as current Ingress:
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cluster-gateway
namespace: gateway-system
spec:
gatewayClassName: istio
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
EOF
Step 3: Deploy with Gateway API
helm upgrade dot-ai \
oci://ghcr.io/vfarcic/dot-ai/charts/dot-ai:0.168.0 \
--namespace dot-ai \
--set ingress.enabled=false \
--set gateway.name=cluster-gateway \
--set gateway.namespace=gateway-system \
--reuse-values
Step 4: Verify and Update DNS
# Get Gateway IP
GATEWAY_IP=$(kubectl get gateway cluster-gateway -n gateway-system -o jsonpath='{.status.addresses[0].value}')
# Update DNS A record to point to Gateway IP
Step 5: Verify Deployment
Verify the deployment through your MCP client (Claude Desktop, Cursor, etc.) by connecting to the configured endpoint. The MCP client will automatically validate the connection and display the server status.
Alternatively, check deployment status:
kubectl get httproute,svc,pod -n dot-ai
Additional Resources
Gateway API:
Integration:
dot-ai Documentation: