CRD Reference¶
This document provides the API reference for Custom Resource Definitions (CRDs) used by the Cloudflare Tunnel Gateway Controller. The controller ships two project-owned CRDs, GatewayClassConfig and ExternalBackend, and watches the standard Gateway API resources.
GatewayClassConfig¶
API Version: cf.k8s.lex.la/v1alpha1 Kind: GatewayClassConfig Scope: Cluster
GatewayClassConfig provides tunnel configuration for the controller. It is referenced by a GatewayClass via spec.parametersRef.
Spec¶
Starting v3 the spec carries only the contract the controller needs for Cloudflare API calls. Proxy-side configuration (tunnel token, replicas, liveness probes) lives in the Helm chart proxy.* values; see Helm chart reference. The AmneziaWG sidecar that v2 attached to the controller-managed cloudflared deployment is not available in v3 — see Upgrading v2 → v3.
| Field | Type | Required | Description |
|---|---|---|---|
tunnelID | string | Yes | Cloudflare Tunnel UUID. Must match the pattern ^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$ |
accountId | string | No | Cloudflare Account ID. If unset, it is read from the account-id key in the credentials Secret; if that key is also absent, it is auto-detected from the Cloudflare API when the token has access to a single account. When set, it must be a 32-character lowercase hexadecimal string (validated by a CRD-level CEL rule) |
cloudflareCredentialsSecretRef | SecretReference | Yes | Reference to the Secret containing the Cloudflare API token |
SecretReference¶
| Field | Type | Default | Description |
|---|---|---|---|
name | string | - | Secret name (required) |
namespace | string | controller namespace | Namespace of the Secret. Defaults to the controller's own namespace; set it to place the Secret in a different namespace |
key | string | api-token | Key within the Secret |
Example¶
apiVersion: cf.k8s.lex.la/v1alpha1
kind: GatewayClassConfig
metadata:
name: cloudflare-tunnel-config
spec:
tunnelID: "550e8400-e29b-41d4-a716-446655440000"
# accountId: "0123456789abcdef0123456789abcdef" # Optional 32-char hex; auto-detected if omitted
cloudflareCredentialsSecretRef:
name: cloudflare-credentials
key: api-token
Status¶
GatewayClassConfig has a status.conditions subresource. The reconciler emits:
SecretsResolved—Truewhen the referenced credentials Secret exists and carries the expected key,Falseotherwise.Valid—Truewhen all validation checks pass;Falsewith the first failure message otherwise.
ExternalBackend¶
API Version: cf.k8s.lex.la/v1alpha1 Kind: ExternalBackend Scope: Namespaced
ExternalBackend defines an out-of-cluster HTTP(S) endpoint that an HTTPRoute or GRPCRoute may target as a backendRef. The in-process L7 proxy ultimately dials a URL, so a route can point at an arbitrary external origin without a Service standing in for it. Unlike a Service of type ExternalName (which only carries a DNS name and infers the scheme from the port), ExternalBackend makes the scheme explicit and lets the host be an address that is not a valid Service name. It is a spec-only resource (no status): a route referencing a missing or malformed ExternalBackend surfaces the failure on the route's own ResolvedRefs condition, mirroring how an unresolvable Service backendRef is reported. See ExternalBackend for usage details.
ExternalBackend Spec¶
| Field | Type | Required | Description |
|---|---|---|---|
scheme | string | Yes | Protocol used to dial the backend: http or https |
host | string | Yes | Backend hostname or IP address (no scheme, port, or path). IPv6 literals must be bracketed, e.g. [2001:db8::1] |
port | integer | Yes | Backend TCP port (1-65535) |
path | string | No | Optional base path prepended to the request path; must begin with /. May include a query string whose parameters merge into every dialed request (the request's own parameters win on a key conflict) |
ExternalBackend Example¶
apiVersion: cf.k8s.lex.la/v1alpha1
kind: ExternalBackend
metadata:
name: external-api
namespace: default
spec:
scheme: https
host: api.example.com
port: 443
path: /v1
Gateway API Resources¶
The controller watches standard Gateway API resources. For their full specification, see the Gateway API documentation.
GatewayClass¶
Standard Gateway API GatewayClass with parametersRef pointing to GatewayClassConfig:
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: cloudflare-tunnel
spec:
controllerName: cf.k8s.lex.la/tunnel-controller
parametersRef:
group: cf.k8s.lex.la
kind: GatewayClassConfig
name: cloudflare-tunnel-config
Gateway¶
Standard Gateway API Gateway:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cloudflare-tunnel
namespace: cloudflare-tunnel-system
spec:
gatewayClassName: cloudflare-tunnel
listeners:
- name: http
port: 80
protocol: HTTP
HTTPRoute¶
Standard Gateway API HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app
namespace: default
spec:
parentRefs:
- name: cloudflare-tunnel
namespace: cloudflare-tunnel-system
hostnames:
- app.example.com
rules:
- backendRefs:
- name: my-service
port: 80
GRPCRoute¶
GRPCRoute is served by the in-process L7 proxy — gRPC service/method matches map onto /{service}/{method} path rules and the upstream hop is h2c. See GRPCRoute for the full field support matrix.
Standard Gateway API GRPCRoute:
apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
name: my-grpc-service
namespace: default
spec:
parentRefs:
- name: cloudflare-tunnel
namespace: cloudflare-tunnel-system
hostnames:
- grpc.example.com
rules:
- backendRefs:
- name: grpc-server
port: 50051
ReferenceGrant¶
Standard Gateway API ReferenceGrant for cross-namespace references:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-cross-namespace
namespace: backend
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: frontend
to:
- group: ""
kind: Service
Status Conditions¶
Gateway Status¶
| Condition | Status | Reason | Description |
|---|---|---|---|
Accepted | True | Accepted | Gateway accepted by controller |
Accepted | False | ListenersNotValid | Gateway has conflicted own listeners (one or more own listeners carry Conflicted: True); per-listener status reports the conflict |
Accepted | False | InvalidParameters | GatewayClassConfig referenced by the GatewayClass cannot be resolved |
Programmed | True | Programmed | Gateway configured in Cloudflare |
Programmed | False | Invalid | GatewayClassConfig referenced by the GatewayClass cannot be resolved |
HTTPRoute/GRPCRoute Status¶
| Condition | Status | Reason | Description |
|---|---|---|---|
Accepted | True | Accepted | Route accepted and synced |
Accepted | False | NoMatchingParent | No listener matched the parentRef's sectionName or port; also fires when hostname is the failure reason and the parentRef pinned a sectionName or port |
Accepted | False | NoMatchingListenerHostname | Route hostnames do not intersect with any listener hostname (no sectionName/port pin on the parentRef) |
Accepted | False | NotAllowedByListeners | Route namespace or kind not allowed by listener |
Accepted | False | Pending | Sync to the Cloudflare Tunnel API failed; reconcile will retry. Proxy-push failures are best-effort: they are logged and counted via the cftunnel_sync_errors_total{error_type="proxy_push"} counter but do not flip Accepted to False / Reason=Pending |
Accepted | False | UnsupportedProtocol | GRPCRoute only: gRPC cannot be served over an explicit proxy.tunnel.protocol: quic tunnel (cloudflared drops HTTP trailers over QUIC, losing grpc-status). Switch to http2, or auto/unset which the proxy upgrades to http2 for gRPC |
Accepted | False | Conflicted | An HTTPRoute and a GRPCRoute conflict on the same Gateway with intersecting hostnames; the oldest Route by creationTimestamp (ties broken by {namespace}/{name}) is accepted and the other is rejected |
ResolvedRefs | True | ResolvedRefs | Backend references resolved |
ResolvedRefs | False | RefNotPermitted | Cross-namespace reference denied |
ResolvedRefs | False | BackendNotFound | Backend Service not found |
ResolvedRefs | False | InvalidKind | Backend ref group/kind is not a core Service |
API Versions¶
| Resource | API Group | Version | Status |
|---|---|---|---|
| GatewayClassConfig | cf.k8s.lex.la | v1alpha1 | Alpha |
| ExternalBackend | cf.k8s.lex.la | v1alpha1 | Alpha |
| GatewayClass | gateway.networking.k8s.io | v1 | GA |
| Gateway | gateway.networking.k8s.io | v1 | GA |
| HTTPRoute | gateway.networking.k8s.io | v1 | GA |
| GRPCRoute | gateway.networking.k8s.io | v1 | GA |
| ReferenceGrant | gateway.networking.k8s.io | v1beta1 | Beta |
Installing CRDs¶
Gateway API CRDs¶
kubectl apply --filename https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.5.1/standard-install.yaml
Project CRDs (GatewayClassConfig and ExternalBackend)¶
Installed automatically by the Helm chart. For manual installation: