Skip to content

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

Helm v1.10.0

Installs the cfgate controller, a Gateway API-native Kubernetes operator for Cloudflare Tunnel, DNS, and Access management.

Chart 1.10.0 installs cfgate 0.2.0-alpha.11.

The chart deploys:

  • Controller Deployment (with health probes, security context, resource limits)
  • CRDs (CloudflareTunnel, CloudflareDNS, CloudflareAccessApplication, CloudflareAccessPolicy)
  • Manager ClusterRole/Binding and namespaced claim Role/Binding
  • ServiceAccount
  • Metrics Service (optional ServiceMonitor for Prometheus)
  • Kubernetes 1.30 or later for the Gateway API bundle below.
  • Helm 3.x or 4.x
  • Gateway API CRDs installed:
Terminal window
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.2/standard-install.yaml

Gateway API CRDs are a cluster-level prerequisite, not a chart dependency. They may already be installed if you run Istio, Cilium, Envoy Gateway, or another Gateway API implementation.

The chart’s kubeVersion floor applies to its templates. The installed Gateway API bundle may require a newer Kubernetes version, as shown above.

Terminal window
helm install cfgate oci://ghcr.io/cfgate/charts/cfgate \
--namespace cfgate-system --create-namespace
Terminal window
helm upgrade cfgate oci://ghcr.io/cfgate/charts/cfgate \
--namespace cfgate-system

This upgrades cfgate 0.2.0-alpha.10 to 0.2.0-alpha.11. Existing chart values and CRD fields remain compatible. Clear or update explicit image overrides to select the new chart-owned digest.

  • namespace matchLabels requires each label to exist, including empty values; add the intended label or use matchNames to include a namespace explicitly
  • HTTPS cannot use h2c, and inherited HTTP/2 and h2c cannot both be enabled; disable the inherited transport explicitly when switching
  • invalid effective route transport retains matching HTTP 503 responses while valid siblings and backend revocations continue to publish
  • HTTPRoute backends require TCP Service ports; UDP-only and SCTP-only ports produce matching HTTP 500 responses

No new CRD fields, chart settings, or connector image are required. Review the actual image overrides and origin settings stored on existing Tunnel resources. Preserve installation identity and pending recovery state through the upgrade. Resolve pending DNS writes and service-token distribution before any rollback. For older installations, follow the versioned 1.8.0 to 1.9.0 migration notes first, including TLS verification overrides and DNS TTL inheritance.

For a temporary controller-only removal:

Terminal window
helm uninstall cfgate --namespace cfgate-system

The chart retains CRDs. Keep custom resources, credential Secrets, ReferenceGrants, and the installation namespace with its ownership claims so the matching controller can resume work after reinstallation. Connector Pods and Cloudflare resources can keep serving existing traffic. Configuration updates, drift repair, token renewal and finalization stop while the controller is absent.

Keep the controller, its RBAC, credentials and grants running through cleanup. Inventory the resources belonging to this installation and check retention and orphan policies before deleting anything.

  1. Remove its routes or attachments and confirm tunnel publication has withdrawn their forwarding. Kubernetes deletion alone does not prove remote withdrawal.
  2. Delete its CloudflareDNS and CloudflareAccessApplication objects. Wait for their finalizers while referenced tunnels and policies remain available.
  3. Delete its CloudflareAccessPolicy objects and wait for policy/token cleanup. Then delete its CloudflareTunnel objects and wait for connector drain and remote tunnel cleanup.
  4. Verify remote resources are removed or deliberately retained with an owner handoff. Only then uninstall the chart and remove unused credentials and claims.
  5. Delete CRDs only when no installation still uses them. Deleting a CRD affects every object of that kind in the cluster.

Select explicit names and namespaces. A chart release does not own every cfgate object in the cluster. If deletion stalls, inspect conditions, events and logs; retain cleanup credentials and grants. Removing finalizers or recovery status bypasses cleanup and can leave Cloudflare resources behind.

The manager requests up to 30 seconds for graceful shutdown. Kubernetes can force termination when the Pod allowance expires; a longer allowance does not replace recovery after interrupted writes. Credentials stored in Secrets also need to be reloaded by their consumers independently.

KeyTypeDefaultDescription
replicaCountint2Number of controller replicas
terminationGracePeriodSecondsint30Pod shutdown allowance; 0 requests immediate termination
image.repositorystringghcr.io/cfgate/cfgateContainer image repository
image.tagstring""Explicit tag opts out of the chart’s default digest pin
image.digeststring""Explicit SHA-256 digest; takes precedence over tag
image.pullPolicystringIfNotPresentImage pull policy
imagePullSecretslist[]Image pull secrets
nameOverridestring""Override chart name
fullnameOverridestring""Override full release name
namespaceOverridestring""Override release namespace

With the default repository and empty image.tag/image.digest, the Deployment uses the verified multi-architecture digest recorded in Chart.yaml’s cfgate.io/operator-image-digest annotation. This chart-owned pin updates with the chart even when upgrading with --reuse-values.

An explicit tag or custom repository preserves tag-based selection unless image.digest is supplied. A custom repository with an empty tag uses appVersion. Custom images are administrator choices and are not certified by the chart’s release guard. Set image.digest: sha256:<64 lowercase hex characters> to pin a custom image; that digest takes precedence over any tag. An explicitly set digest persists with reused values, so update or clear it during upgrades.

Controller limits and installation identity

Section titled “Controller limits and installation identity”
KeyTypeDefaultManager flag
controller.clusterDomainstringcluster.local--cluster-domain
controller.installationNamespacestring"" (actual Pod namespace)--installation-namespace, when set
controller.cloudflareRequestTimeoutSecondsint30--cloudflare-request-timeout=30s
controller.maxIngressRulesint1000--max-ingress-rules
controller.maxConfigurationBytesint1048576--max-configuration-bytes

Timeout values are positive whole seconds, with a maximum of 9223372036 seconds (the manager’s duration representation limit). Rule limits accept integers from 1 to 2147483647; byte limits accept 67 to 2147483647 so the controller can publish an emergency denial. These are operator work limits, not Cloudflare service limits; exceeding them replaces tunnel forwarding with HTTP 503 until the configuration fits. The rule budget includes the fallback rule. The byte budget includes serialized ingress and origin settings.

clusterDomain accepts a lowercase DNS suffix without a trailing dot. Use the suffix actually configured in the cluster; setting the value does not reconfigure cluster DNS. For example:

controller:
clusterDomain: cluster.internal
cloudflareRequestTimeoutSeconds: 20
maxIngressRules: 2000
maxConfigurationBytes: 2097152

POD_NAMESPACE comes from the Pod’s actual metadata.namespace, so namespaceOverride also selects the default installation namespace correctly. The namespace’s Kubernetes UID and each resource UID participate in ownership. Preserve that namespace across upgrades. Setting controller.installationNamespace selects an existing namespace for identity and Tunnel/Access claims; it does not create one. Changing that value, deleting/recreating the namespace, or recreating owned CRs requires the documented ownership migration.

KeyTypeDefaultDescription
installCRDsbooltrueInstall CRDs with the chart
rbac.createbooltrueCreate manager ClusterRole/Binding and installation claim Role/Binding
serviceAccount.createbooltrueCreate ServiceAccount
serviceAccount.namestring""ServiceAccount name (generated if empty)
serviceAccount.annotationsobject{}ServiceAccount annotations

The manager’s namespaced claim Role grants get/create/delete on ConfigMaps in controller.installationNamespace, or the actual manager namespace when unset. Its RoleBinding targets the manager’s service account, even when the claim namespace differs. A custom installation namespace must already exist. The ClusterRole retains get/list/watch on Pods for connector drain checks, but grants no ConfigMap access.

When rbac.create=false, supply both the cluster-wide manager permissions and the namespaced claim permissions yourself. Remove any previous cluster-wide ConfigMap grant; adding a namespaced Role alone does not revoke it. This scopes ConfigMap access to a namespace, not individual claims, and leaves other required manager permissions unchanged. Do not disable the manager’s service-account token: it needs Kubernetes API access. Connector Pods independently disable unused token mounting.

Access-required routing remains an explicit per-HTTPRoute opt-in (cfgate.io/access-required: namespace/name), not a chart-wide setting. Its dependency receipts are included in the Tunnel CRD. The selected tunnel credential needs Access application/policy read permissions. See the Access-required contract and limits; edge configuration is asynchronous, and strict or gRPC authentication requires origin-side enforcement.

KeyTypeDefaultDescription
metrics.portint8080Metrics endpoint port
metrics.service.enabledbooltrueCreate metrics Service
metrics.service.portint8080Metrics Service port
metrics.service.annotationsobject{}Metrics Service annotations
metrics.serviceMonitor.enabledboolfalseCreate Prometheus ServiceMonitor
metrics.serviceMonitor.namespacestring""ServiceMonitor namespace (defaults to release namespace)
metrics.serviceMonitor.intervalstring30sScrape interval
metrics.serviceMonitor.labelsobject{}Additional ServiceMonitor labels
KeyTypeDefaultDescription
health.portint8081Health probe port (/healthz, /readyz)
KeyTypeDefaultDescription
resources.requests.cpustring100mCPU request
resources.requests.memorystring128MiMemory request
resources.limits.cpustring500mCPU limit
resources.limits.memorystring256MiMemory limit
nodeSelectorobject{}Node selector
tolerationslist[]Tolerations
affinityobject{}Affinity rules
podAnnotationsobject{}Pod annotations
podLabelsobject{}Pod labels
KeyTypeDefaultDescription
securityContext.allowPrivilegeEscalationboolfalseDisallow privilege escalation
securityContext.capabilities.droplist[ALL]Drop all capabilities
securityContext.readOnlyRootFilesystembooltrueRead-only root filesystem
podSecurityContext.runAsNonRootbooltrueRun as non-root
podSecurityContext.seccompProfile.typestringRuntimeDefaultSeccomp profile

The chart defaults to two controller replicas. To scale further:

replicaCount: 3

Leader election is always enabled via --leader-elect, so only one replica actively reconciles at a time while standby replicas take over if the leader fails. Replicas alone do not ensure placement on different nodes; configure affinity for the failure domains your deployment requires.

If you run the Prometheus Operator:

metrics:
serviceMonitor:
enabled: true
labels:
release: prometheus # match your Prometheus selector

If you use annotation-based scraping instead of ServiceMonitor:

metrics:
service:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"

After installing the chart, see the cfgate documentation for:

  • Creating CloudflareTunnel, CloudflareDNS, CloudflareAccessApplication, and CloudflareAccessPolicy resources
  • Setting up Gateway API GatewayClass and Gateway
  • Configuring HTTPRoute annotations for per-route origin settings
  • Multi-zone DNS configuration

The chart is published at oci://ghcr.io/cfgate/charts/cfgate and listed on Artifact Hub.

See CONTRIBUTING.md for chart-project synchronization, CRD regeneration, and RBAC updates.