Skip to content

cfgate.iocfgate v0.2.0-alpha.11 · Release documentation

Annotations Reference

Complete reference for all cfgate annotations.

Per-route configuration applied to Gateway API HTTPRoute resources.

AnnotationValuesDefaultDescription
cfgate.io/origin-protocolhttp, httpshttpBackend protocol
cfgate.io/origin-ssl-verifytrue, falseinheritedTLS certificate verification
cfgate.io/origin-connect-timeoutDuration string (30s, 1m)30sOrigin connection timeout
cfgate.io/origin-http-host-headerHostname stringnoneHost header override sent to origin
cfgate.io/origin-server-nameHostname stringnoneTLS SNI server name
cfgate.io/origin-ca-poolManaged file pathnoneCA certificate pool path
cfgate.io/origin-http2true, falseinheritedHTTP/2 to origin
cfgate.io/origin-h2ctrue, falseinheritedHTTP/2 cleartext (h2c) to origin
cfgate.io/ttl1-86400inheritedDNS record TTL in seconds
cfgate.io/cloudflare-proxiedtrue, falsetrueCloudflare proxy (orange cloud)
cfgate.io/access-policyname or namespace/namenoneDeprecated: resolves a policy reference for status only
cfgate.io/hostnameRFC 1123 hostnamenoneOverride the route hostname

Default for cfgate.io/origin-protocol: http


Specifies the protocol used to connect from cloudflared to the backend service.

Valid values: http, https

Default: http

Read by: CloudflareTunnel controller (via route collection), cloudflared-builder

Protocol values are case-insensitive: HTTPS and https both select TLS to the origin. Invalid values are rejected. For a private CA, configure the tunnel’s CA Secret and keep certificate verification enabled. See origin parameters for the connector contract.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: secure-backend
namespace: default
annotations:
cfgate.io/origin-protocol: "https"
spec:
parentRefs:
- name: cloudflare-tunnel
namespace: cfgate-system
hostnames:
- secure.example.com
rules:
- backendRefs:
- name: my-service
port: 443

Controls whether cloudflared verifies the TLS certificate presented by the origin server.

Valid values: true, false, 1, 0, yes, no (case-insensitive)

Default: inherited from tunnel defaults

Read by: CloudflareTunnel controller (via route collection), cloudflared-builder

Omission inherits the tunnel’s originDefaults.noTLSVerify setting. Without an insecure tunnel default, certificates are verified. Explicit true enables verification even under an insecure tunnel default; explicit false disables it for this route. Prefer a trusted CA bundle for private certificates.

metadata:
annotations:
cfgate.io/origin-protocol: "https"
cfgate.io/origin-ssl-verify: "false"

Maximum time cloudflared waits to establish a connection to the origin server.

Valid values: Go duration string (e.g., 30s, 1m, 1h30m)

Default: inherited from tunnel defaults (otherwise 30s)

Use a positive whole number of seconds, such as 10s or 1m. Values that would be rounded or replaced by an SDK fallback are rejected.

Read by: CloudflareTunnel controller (via route collection), cloudflared-builder

metadata:
annotations:
cfgate.io/origin-connect-timeout: "10s"

Overrides the HTTP Host header sent to the origin server. Useful when the origin expects a specific hostname that differs from the public-facing hostname.

Valid values: Hostname string

Default: Not set (uses the route hostname)

Read by: CloudflareTunnel controller (via route collection), cloudflared-builder

metadata:
annotations:
cfgate.io/origin-http-host-header: "internal-service.local"

Specifies the TLS SNI (Server Name Indication) server name for the connection to the origin. Used when the origin’s TLS certificate is issued for a different name than the connecting hostname.

Valid values: Hostname string

Default: Not set

Read by: CloudflareTunnel controller (via route collection), cloudflared-builder

metadata:
annotations:
cfgate.io/origin-server-name: "real-cert-name.internal"

Literal in-container path to a CA certificate pool file used to verify the origin server’s TLS certificate. For alpha.5, cfgate accepts only its managed mount path, /etc/cfgate/origin-ca-pool/ca.pem, and only when the referenced CloudflareTunnel has spec.originDefaults.caPoolSecretRef configured.

Use spec.originDefaults.caPoolSecretRef on CloudflareTunnel when cfgate should mount a Kubernetes Secret. That managed Secret mount is available at /etc/cfgate/origin-ca-pool/ca.pem.

Valid values: /etc/cfgate/origin-ca-pool/ca.pem

Default: Not set (system CA pool)

Read by: CloudflareTunnel controller (via route collection), cloudflared-builder

metadata:
annotations:
cfgate.io/origin-ca-pool: "/etc/cfgate/origin-ca-pool/ca.pem"

Enables HTTP/2 for the connection between cloudflared and the origin server.

Valid values: true, false, 1, 0, yes, no (case-insensitive)

Default: inherited from tunnel defaults (otherwise false)

Read by: CloudflareTunnel controller (via route collection), cloudflared-builder

metadata:
annotations:
cfgate.io/origin-http2: "true"

Enables HTTP/2 cleartext (h2c) for the connection between cloudflared and the origin server. Use this for backends that speak HTTP/2 without TLS, such as Envoy sidecars or other h2c-speaking services.

Valid values: true, false, 1, 0, yes, no (case-insensitive)

Default: inherited from tunnel defaults (otherwise false)

Read by: CloudflareTunnel controller (via route collection), cloudflared-builder

Explicit false disables inherited h2c. When switching from a tunnel-wide HTTP/2 default, also set cfgate.io/origin-http2: "false"; both transports cannot be enabled together. Requires the inherent-design/cloudflared fork image (the default). Upstream cloudflared silently ignores this field. See Image.

The effective combination is checked against the referenced tunnel for each parent. HTTPS with h2c and simultaneous HTTP/2 plus h2c are invalid. Such routes report Accepted=False and retain matching HTTP 503 responses, allowing valid sibling routes and backend revocations to publish. Invalid forwarding fallbacks also become HTTP 503 responses. A stored Cloudflare configuration and a ready connector Pod do not alone prove that an origin request succeeds.

HTTPRoute backends require a TCP Service port. UDP-only and SCTP-only ports report ResolvedRefs=False with reason UnsupportedProtocol and retain matching HTTP 500 responses. An omitted Service protocol uses Kubernetes’ TCP default.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http2-backend
namespace: default
annotations:
cfgate.io/origin-h2c: "true"
spec:
parentRefs:
- name: cloudflare-tunnel
namespace: cfgate-system
hostnames:
- h2c.example.com
rules:
- backendRefs:
- name: http2-service
port: 50051

Sets the DNS record TTL (Time To Live) in seconds. Value 1 is special and means “auto” (Cloudflare-managed TTL). Only relevant when CloudflareDNS discovers this route via gatewayRoutes.

Valid values: Integer from 1 to 86400

Default: inherits CloudflareDNS.spec.defaults.ttl (otherwise Auto)

Read by: CloudflareDNS controller (via route hostname collection). See CloudflareDNS for default TTL configuration.

metadata:
annotations:
cfgate.io/ttl: "300"

Controls whether Cloudflare proxies traffic for the DNS record (the “orange cloud” toggle). When true, traffic passes through Cloudflare’s network (DDoS protection, WAF, caching). When false, DNS resolves directly to the tunnel domain.

Valid values: true, false, 1, 0, yes, no (case-insensitive)

Default: true

Read by: CloudflareDNS controller (via route hostname collection). See CloudflareDNS for default proxy configuration.

metadata:
annotations:
cfgate.io/cloudflare-proxied: "false"

Deprecated. Resolves a CloudflareAccessPolicy reference for HTTPRoute status and warnings only. It does not create Cloudflare Access Applications and does not link policies. Use CloudflareAccessApplication to protect Gateway API targets.

Valid values: name (same namespace) or namespace/name

Default: Not set

Read by: HTTPRoute controller for deprecated status resolution only

metadata:
annotations:
cfgate.io/access-policy: "my-access-policy"

Or cross-namespace:

metadata:
annotations:
cfgate.io/access-policy: "cfgate-system/shared-policy"

Sets or overrides the hostname for an HTTPRoute. When set, it overrides spec.hostnames.

Valid values: RFC 1123 hostname (max 253 characters, labels max 63 characters, lowercase alphanumeric and hyphens)

Default: Not set

Read by: HTTPRoute-driven reconciliation paths

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app
annotations:
cfgate.io/hostname: "app.example.com"
spec:
parentRefs:
- name: cloudflare-tunnel
namespace: cfgate-system
hostnames:
- ignored.example.com
rules:
- backendRefs:
- name: my-service
port: 80

Applied to Gateway resources to connect them to CloudflareTunnel resources.

AnnotationValuesDescription
cfgate.io/tunnel-refnamespace/name or nameReferences the CloudflareTunnel resource this Gateway should use
cfgate.io/tunnel-targetTunnel domain (e.g., uuid.cfargotunnel.com)Set by controller (read-only)

Connects a Gateway to a CloudflareTunnel resource. This annotation is the link between the Gateway API layer and cfgate’s tunnel management.

Format: namespace/name (recommended) or name (same namespace)

Read by: CloudflareTunnel controller (finds Gateways referencing this tunnel), CloudflareDNS controller (finds Gateways for route discovery), CloudflareAccessApplication controller (credential inheritance)

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cloudflare-tunnel
namespace: cfgate-system
annotations:
cfgate.io/tunnel-ref: cfgate-system/my-tunnel
spec:
gatewayClassName: cfgate
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All

The tunnel endpoint domain, set automatically by the CloudflareTunnel controller after tunnel creation. Format is {tunnelID}.cfargotunnel.com. Do not set this manually.

Read by: Gateway controller, DNS controllers


Applied to CloudflareTunnel, CloudflareDNS, CloudflareAccessPolicy, and CloudflareAccessApplication resources to control deletion behavior.

AnnotationValuesDefaultDescription
cfgate.io/deletion-policyorphanNot set (full cleanup)When set to orphan, skips Cloudflare-side cleanup on resource deletion

Controls what happens to Cloudflare-side resources when the Kubernetes resource is deleted.

Valid values: orphan

Default: Not set (the controller deletes the corresponding Cloudflare resource during finalization)

Supported on:

  • CloudflareTunnel: When set to orphan, the tunnel remains in Cloudflare but the K8s resource is removed. The controller skips tunnel deletion and proceeds directly to finalizer removal.
  • CloudflareDNS: When set to orphan, the DNS records remain in Cloudflare but the K8s resource is removed. The controller skips record cleanup and proceeds directly to finalizer removal.
  • CloudflareAccessPolicy: When set to orphan, the reusable Access policy and service tokens remain in Cloudflare. The controller skips cleanup and proceeds directly to finalizer removal.
  • CloudflareAccessApplication: When set to orphan, Access Applications and the per-resource owner tag remain in Cloudflare. The controller skips application and tag cleanup and proceeds directly to finalizer removal.

Use cases:

  • Migrating resources between clusters (delete from old cluster without destroying the Cloudflare resource)
  • Debugging tunnel or access issues (remove K8s resource without affecting live traffic)
  • Emergency finalizer unblocking (annotate a stuck resource, then delete it)
Terminal window
# Annotate before deletion to orphan the tunnel
kubectl annotate cloudflaretunnel my-tunnel cfgate.io/deletion-policy=orphan
# Now delete: tunnel stays in Cloudflare, K8s resource removed
kubectl delete cloudflaretunnel my-tunnel
Terminal window
# Same for DNS resources
kubectl annotate cloudflarednses my-dns cfgate.io/deletion-policy=orphan
kubectl delete cloudflaredns my-dns
Terminal window
# Same for access policies
kubectl annotate cloudflareaccesspolicy my-policy cfgate.io/deletion-policy=orphan
kubectl delete cloudflareaccesspolicy my-policy
Terminal window
# Same for access applications
kubectl annotate cloudflareaccessapplication my-app cfgate.io/deletion-policy=orphan
kubectl delete cloudflareaccessapplication my-app

Orphaned Cloudflare resources remain your responsibility. Recreating a Kubernetes object with the same name creates a new UID and does not restore ownership. Follow the adoption and ownership-transfer procedure before resuming management, or delete the remote resources manually after verifying that no active owner uses them.


Applied to CloudflareDNS resources to control DNS-specific behavior.

AnnotationValuesDefaultDescription
cfgate.io/allow-deep-subdomainstrueNot set (warning emitted)Suppresses the DeepSubdomain warning event for multi-level subdomains

Suppresses the DeepSubdomain warning event that the controller emits when a hostname has more than one subdomain level relative to its zone. For example, api.staging.example.com in zone example.com has two subdomain levels and triggers the warning because Cloudflare Universal SSL certificates cover only single-level wildcards (*.example.com). Deeper subdomains require Cloudflare Advanced Certificate Manager or a custom certificate.

Valid values: "true" to suppress the warning; any other value or absent means the warning is emitted.

Default: Not set (warning emitted on each reconciliation)

Applied to: CloudflareDNS resources

Read by: CloudflareDNS controller (checked in syncRecords before emitting the DeepSubdomain event)

Set this annotation on the CloudflareDNS resource when you have appropriate TLS coverage for deep subdomains and want to suppress the recurring warning:

Terminal window
kubectl annotate cloudflarednses my-dns -n cfgate-system \
cfgate.io/allow-deep-subdomains=true

See the Multi-level Subdomains section in the CloudflareDNS reference for details on subdomain depth validation.


These annotations are managed by cfgate controllers and should not be set manually.

AnnotationApplied ToDescription
cfgate.io/config-hashCloudflareTunnelSHA-256 hash of the last-synced tunnel configuration. Used to skip redundant Cloudflare API updates when the configuration has not changed.

The annotationFilter field on CloudflareDNS.spec.source.gatewayRoutes is not itself a cfgate annotation. It is a CRD spec field that accepts any user-chosen annotation key (or key=value pair) as a filter for route discovery.

When set, only HTTPRoutes bearing the specified annotation will be included in DNS sync. The annotation name and value are entirely user-defined.

Supported formats:

  • key=value: Only syncs routes where the annotation key exists AND the value matches exactly
  • key: Only syncs routes where the annotation key exists (any value)

Example: Using cfgate.io/dns-sync=enabled as a convention (but any annotation works):

apiVersion: cfgate.io/v1alpha1
kind: CloudflareDNS
metadata:
name: my-dns
namespace: cfgate-system
spec:
tunnelRef:
name: my-tunnel
zones:
- name: example.com
source:
gatewayRoutes:
enabled: true
annotationFilter: "cfgate.io/dns-sync=enabled"

Then only HTTPRoutes with the matching annotation are synced:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: synced-app
annotations:
cfgate.io/dns-sync: "enabled" # Matches filter, DNS record created
spec:
parentRefs:
- name: cloudflare-tunnel
namespace: cfgate-system
hostnames:
- app.example.com
rules:
- backendRefs:
- name: my-service
port: 80

Routes without the annotation are ignored by this CloudflareDNS resource, even if they reference the same Gateway and tunnel.