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)
Prerequisites
Section titled “Prerequisites”- Kubernetes 1.30 or later for the Gateway API bundle below.
- Helm 3.x or 4.x
- Gateway API CRDs installed:
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.2/standard-install.yamlGateway 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.
Install
Section titled “Install”helm install cfgate oci://ghcr.io/cfgate/charts/cfgate \ --namespace cfgate-system --create-namespaceUpgrade
Section titled “Upgrade”helm upgrade cfgate oci://ghcr.io/cfgate/charts/cfgate \ --namespace cfgate-systemUpgrade from 1.9.0 to 1.10.0
Section titled “Upgrade from 1.9.0 to 1.10.0”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
matchLabelsrequires each label to exist, including empty values; add the intended label or usematchNamesto 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.
Controller removal
Section titled “Controller removal”For a temporary controller-only removal:
helm uninstall cfgate --namespace cfgate-systemThe 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.
Full decommissioning
Section titled “Full decommissioning”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.
- Remove its routes or attachments and confirm tunnel publication has withdrawn their forwarding. Kubernetes deletion alone does not prove remote withdrawal.
- Delete its CloudflareDNS and CloudflareAccessApplication objects. Wait for their finalizers while referenced tunnels and policies remain available.
- Delete its CloudflareAccessPolicy objects and wait for policy/token cleanup. Then delete its CloudflareTunnel objects and wait for connector drain and remote tunnel cleanup.
- Verify remote resources are removed or deliberately retained with an owner handoff. Only then uninstall the chart and remove unused credentials and claims.
- 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.
Configuration
Section titled “Configuration”Controller
Section titled “Controller”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.
| Key | Type | Default | Description |
|---|---|---|---|
replicaCount | int | 2 | Number of controller replicas |
terminationGracePeriodSeconds | int | 30 | Pod shutdown allowance; 0 requests immediate termination |
image.repository | string | ghcr.io/cfgate/cfgate | Container image repository |
image.tag | string | "" | Explicit tag opts out of the chart’s default digest pin |
image.digest | string | "" | Explicit SHA-256 digest; takes precedence over tag |
image.pullPolicy | string | IfNotPresent | Image pull policy |
imagePullSecrets | list | [] | Image pull secrets |
nameOverride | string | "" | Override chart name |
fullnameOverride | string | "" | Override full release name |
namespaceOverride | string | "" | 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”| Key | Type | Default | Manager flag |
|---|---|---|---|
controller.clusterDomain | string | cluster.local | --cluster-domain |
controller.installationNamespace | string | "" (actual Pod namespace) | --installation-namespace, when set |
controller.cloudflareRequestTimeoutSeconds | int | 30 | --cloudflare-request-timeout=30s |
controller.maxIngressRules | int | 1000 | --max-ingress-rules |
controller.maxConfigurationBytes | int | 1048576 | --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: 2097152POD_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.
CRDs and RBAC
Section titled “CRDs and RBAC”| Key | Type | Default | Description |
|---|---|---|---|
installCRDs | bool | true | Install CRDs with the chart |
rbac.create | bool | true | Create manager ClusterRole/Binding and installation claim Role/Binding |
serviceAccount.create | bool | true | Create ServiceAccount |
serviceAccount.name | string | "" | ServiceAccount name (generated if empty) |
serviceAccount.annotations | object | {} | 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.
Metrics and Monitoring
Section titled “Metrics and Monitoring”| Key | Type | Default | Description |
|---|---|---|---|
metrics.port | int | 8080 | Metrics endpoint port |
metrics.service.enabled | bool | true | Create metrics Service |
metrics.service.port | int | 8080 | Metrics Service port |
metrics.service.annotations | object | {} | Metrics Service annotations |
metrics.serviceMonitor.enabled | bool | false | Create Prometheus ServiceMonitor |
metrics.serviceMonitor.namespace | string | "" | ServiceMonitor namespace (defaults to release namespace) |
metrics.serviceMonitor.interval | string | 30s | Scrape interval |
metrics.serviceMonitor.labels | object | {} | Additional ServiceMonitor labels |
Health Probes
Section titled “Health Probes”| Key | Type | Default | Description |
|---|---|---|---|
health.port | int | 8081 | Health probe port (/healthz, /readyz) |
Pod Scheduling
Section titled “Pod Scheduling”| Key | Type | Default | Description |
|---|---|---|---|
resources.requests.cpu | string | 100m | CPU request |
resources.requests.memory | string | 128Mi | Memory request |
resources.limits.cpu | string | 500m | CPU limit |
resources.limits.memory | string | 256Mi | Memory limit |
nodeSelector | object | {} | Node selector |
tolerations | list | [] | Tolerations |
affinity | object | {} | Affinity rules |
podAnnotations | object | {} | Pod annotations |
podLabels | object | {} | Pod labels |
Security
Section titled “Security”| Key | Type | Default | Description |
|---|---|---|---|
securityContext.allowPrivilegeEscalation | bool | false | Disallow privilege escalation |
securityContext.capabilities.drop | list | [ALL] | Drop all capabilities |
securityContext.readOnlyRootFilesystem | bool | true | Read-only root filesystem |
podSecurityContext.runAsNonRoot | bool | true | Run as non-root |
podSecurityContext.seccompProfile.type | string | RuntimeDefault | Seccomp profile |
High Availability
Section titled “High Availability”The chart defaults to two controller replicas. To scale further:
replicaCount: 3Leader 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.
Monitoring
Section titled “Monitoring”Prometheus ServiceMonitor
Section titled “Prometheus ServiceMonitor”If you run the Prometheus Operator:
metrics: serviceMonitor: enabled: true labels: release: prometheus # match your Prometheus selectorPrometheus Annotations
Section titled “Prometheus Annotations”If you use annotation-based scraping instead of ServiceMonitor:
metrics: service: annotations: prometheus.io/scrape: "true" prometheus.io/port: "8080"Next Steps
Section titled “Next Steps”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
Artifact Hub
Section titled “Artifact Hub”The chart is published at oci://ghcr.io/cfgate/charts/cfgate and listed on
Artifact Hub.
Chart Development
Section titled “Chart Development”See CONTRIBUTING.md for chart-project synchronization, CRD regeneration, and RBAC updates.