Envoy Gateway CRDs Chart
envoy-gateway-crds is the cluster API lifecycle release for Envoy Gateway
v1.9.0. It installs the 8 Envoy Gateway extension CRDs, the 10 Gateway API
v1.6.1 Experimental CRDs, and an optional Gateway API safe-upgrade policy.
The initial lifecycle chart release is 1.0.0. It must be published before the Envoy Gateway application chart 2.0.0 migration release.
Install this chart before the Envoy Gateway application chart.
The release boundary lets Kubernetes discovery register every API before Helm
or helm-diff validates application custom resources.
Compatibility
| Component | Supported version |
|---|---|
| Envoy Gateway | v1.9.0 |
| Envoy Gateway CRDs | v1.9.0 |
| Gateway API | v1.6.1 |
| Gateway API channel | Experimental |
| Kubernetes | 1.33 through 1.36 |
Envoy Gateway v1.9 does not use Gateway API v1.5.1. Kubernetes versions outside 1.33 through 1.36 are rejected by chart metadata rather than treated as untested-but-supported.
Why The CRDs Are A Separate Release
Helm installs files from crds/ before normal templates, but deliberately does
not track them in the release manifest and does not upgrade or delete them.
Client-side dry-run also cannot add APIs to Kubernetes discovery.
Keeping CRDs as an application dependency would leave CRDs and custom resources
inside the same Helm operation. A first helm diff could still fail with
no matches for kind because the APIs do not exist yet.
The supported order is:
- Install or update
envoy-gateway-crds. - Wait for all selected CRDs to become
Established. - Run application diff with normal Kubernetes validation.
- Install or upgrade Envoy Gateway with
crds.enabled=false.
Fresh Installation
HTTPS repository
helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm upgrade --install envoy-gateway-crds \
helmforge/envoy-gateway-crds \
--version CRD_CHART_VERSION \
--namespace envoy-gateway \
--create-namespace
OCI registry
helm upgrade --install envoy-gateway-crds \
oci://ghcr.io/helmforgedev/helm/envoy-gateway-crds \
--version CRD_CHART_VERSION \
--namespace envoy-gateway \
--create-namespace
Wait for discovery:
helm show crds oci://ghcr.io/helmforgedev/helm/envoy-gateway-crds \
--version CRD_CHART_VERSION > envoy-gateway-crds-installed.yaml
kubectl wait --for=condition=Established \
--timeout=120s \
-f envoy-gateway-crds-installed.yaml
kubectl api-resources --api-group=gateway.networking.k8s.io
kubectl api-resources --api-group=gateway.envoyproxy.io
The rendered file contains all 18 CRDs from the pinned default chart bundle, so this is an exhaustive readiness check rather than a two-object spot check.
Diff and install the application:
export APPLICATION_CHART_VERSION=2.0.0
helm diff upgrade envoy-gateway \
oci://ghcr.io/helmforgedev/helm/envoy-gateway \
--version "${APPLICATION_CHART_VERSION}" \
--namespace envoy-gateway \
--allow-unreleased \
--set crds.enabled=false
helm upgrade --install envoy-gateway \
oci://ghcr.io/helmforgedev/helm/envoy-gateway \
--version "${APPLICATION_CHART_VERSION}" \
--namespace envoy-gateway \
--set crds.enabled=false
Do not add --disable-validation. A missing or incompatible bundle should stop
the application operation before normal manifests are applied.
Installed Resources
The default release installs:
- 10
gateway.networking.k8s.iov1.6.1 Experimental CRDs. - 8
gateway.envoyproxy.iov1.9.0 CRDs. ValidatingAdmissionPolicy/safe-upgrades.gateway.networking.k8s.io.ValidatingAdmissionPolicyBinding/safe-upgrades.gateway.networking.k8s.io.
The chart creates no Pod, Deployment, Service, credential, or network listener.
Configuration
| Value | Default | Purpose |
|---|---|---|
crds.gatewayAPI.enabled |
true |
Select the Gateway API v1.6.1 Experimental bundle |
crds.envoyGateway.enabled |
true |
Select the Envoy Gateway v1.9.0 extension bundle |
safeUpgradePolicy.management |
managed |
Set policy ownership to managed, external, or disabled |
commonLabels |
{} |
Add labels to Helm-managed policy resources |
At least one CRD bundle must remain enabled. managed policy mode requires the
Gateway API bundle because the policy protects those CRDs.
CRD Ownership And Release Size
CRDs remain in Helm’s special crds/ directory. They are intentionally absent
from helm get manifest and are not given misleading Helm release ownership
annotations.
This design keeps the release Secret well below Kubernetes’ 1 MiB object limit. Lifecycle ownership is expressed by:
- The standalone release and compatibility matrix.
- Gateway API version/channel annotations.
- Envoy Gateway bundle version and source digest annotations.
- Server-side apply field manager
helmforge-envoy-gateway-crds. - Real Helm ownership of the policy and binding.
Upgrading CRDs
helm upgrade alone does not update objects from crds/. Render CRDs only,
run server-side dry-run, apply, wait for Established, then update the release
record with --skip-crds:
helm template envoy-gateway-crds \
oci://ghcr.io/helmforgedev/helm/envoy-gateway-crds \
--version CRD_CHART_VERSION \
--include-crds \
--set safeUpgradePolicy.management=disabled \
> envoy-gateway-crds.yaml
kubectl apply \
--server-side \
--dry-run=server \
--field-manager=helmforge-envoy-gateway-crds \
-f envoy-gateway-crds.yaml
kubectl apply \
--server-side \
--field-manager=helmforge-envoy-gateway-crds \
-f envoy-gateway-crds.yaml
kubectl wait --for=condition=Established \
--timeout=120s \
-f envoy-gateway-crds.yaml
helm upgrade envoy-gateway-crds \
oci://ghcr.io/helmforgedev/helm/envoy-gateway-crds \
--version CRD_CHART_VERSION \
--namespace envoy-gateway \
--skip-crds
Never use --force-conflicts as a routine flag. Inspect managedFields, the
installed bundle annotations, and each conflicting path before making an
explicit ownership decision.
CRD upgrades are forward-only. A Helm rollback does not and must not apply an older schema over stored custom resources.
Existing Envoy Gateway 1.10.1 Users
Do not install the standalone policy over an application release that already
owns safe-upgrades.gateway.networking.k8s.io. Do not disable the old CRD
dependency directly from chart 1.10.1.
Wait until the matching Envoy Gateway bridge release is available, then use this mandatory sequence:
- Upgrade the application to the bridge with
crds.enabled=true. - Verify
helm.sh/resource-policy=keepon the policy and binding. - Capture all CRD and custom-resource names, UIDs, and counts.
- Apply the identical standalone bundle with server-side apply.
- Install this release with
--skip-crdsand policy modedisabled. - Upgrade the application bridge with
crds.enabled=false. - Verify every CRD and custom-resource UID is unchanged.
- Adopt the policy and binding into this release.
- Revalidate controller readiness, Gateway status, and traffic.
The bridge is required because Helm decides deletions from the previous release
manifest. Patching only the live policy does not place keep into that manifest.
With Helm 3.17 or later, ownership adoption uses:
helm upgrade envoy-gateway-crds \
oci://ghcr.io/helmforgedev/helm/envoy-gateway-crds \
--namespace envoy-gateway \
--skip-crds \
--take-ownership \
--set safeUpgradePolicy.management=managed
Older Helm clients can patch only the standard Helm ownership label and annotations after comparing the live and target policy specifications. Never edit Helm release Secrets and never use a hook to transfer ownership.
After adoption, do not roll the application back to a revision from before the
bridge. That revision attempts to add the policy back to the old release. Apply
the required compatible application version with crds.enabled=false instead.
The full command-by-command procedure is also published in the Envoy Gateway application documentation.
Provider-Managed Gateway API
A Kubernetes provider or platform release can own Gateway API while HelmForge owns only the Envoy extension CRDs. Verify that the provider exposes the full v1.6.1 Experimental API set and annotations:
kubectl get crd gateways.gateway.networking.k8s.io \
-o go-template='version={{ index .metadata.annotations "gateway.networking.k8s.io/bundle-version" }} channel={{ index .metadata.annotations "gateway.networking.k8s.io/channel" }}{{ "\n" }}'
Then install Envoy CRDs only:
helm upgrade --install envoy-gateway-crds \
oci://ghcr.io/helmforgedev/helm/envoy-gateway-crds \
--version CRD_CHART_VERSION \
--namespace envoy-gateway \
--create-namespace \
--set crds.gatewayAPI.enabled=false \
--set crds.envoyGateway.enabled=true \
--set safeUpgradePolicy.management=external
The official Gateway API Experimental bundle also installs its own safe-upgrade
policy and gateway.networking.x-k8s.io resources. Leaving policy management in
external mode avoids duplicate ownership. A Standard bundle is not equivalent
to the required Experimental contract.
Helmfile And GitOps
Initial bootstrap is always two-phase. A Helmfile needs relationship documents
ordering, but a global client-side diff cannot discover APIs that have not yet
been applied.
Apply the CRD release selector first, wait for discovery, and only then diff or sync the application selector. For later upgrades, apply CRD schemas before the controller release.
Uninstall
Uninstalling this release preserves:
- All selected CRDs.
- All custom resources stored through those CRDs.
- The managed safe-upgrade policy.
- The managed policy binding.
This behavior prevents cluster-wide data loss. Manual CRD deletion is outside the chart lifecycle because deleting a CRD deletes every custom resource of that kind.
Troubleshooting
Application diff reports unknown kinds
Wait for every required CRD to become Established, verify both API groups with
kubectl api-resources, and retry without --disable-validation.
External bundle is rejected
Check all 18 served GVKs. Gateway API CRDs must report bundle v1.6.1 and
channel experimental; Envoy CRDs must report bundle v1.9.0.
Helm reports policy ownership conflict
An existing application or platform release owns the policy. Existing
HelmForge users must complete the bridge migration. Provider-managed users must
select safeUpgradePolicy.management=external.
CRDs did not change after helm upgrade
This is expected for objects under crds/. Use the documented server-side
apply flow before upgrading the controller.
Uninstall left cluster-scoped objects
This is the documented safety contract. Verify that no controller or custom resource uses the APIs before considering a manual deletion.
Security Scan
Kubescape is not applicable because the chart renders no Pod or workload
controller. The deterministic inventory contains only
CustomResourceDefinition, ValidatingAdmissionPolicy, and
ValidatingAdmissionPolicyBinding.