Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix Now×
Skip to content
World desk6 min

13.3 Ensure CRDs Are Installed First: A Kubernetes Deployment Order Guide

Install CRDs first, wait for discovery, make the controller ready, and only then apply Custom Resources. This guide covers kubectl, Helm, Argo CD, Flux, upgrades, and failure diagnosis.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Install and verify a CustomResourceDefinition (CRD) before applying any Custom Resource (CR) that uses it. Then make sure the controller or operator is running before you expect reconciliation. Otherwise Kubernetes commonly returns no matches for kind, discovery errors, or accepts an object that never becomes healthy.

The dependable sequence is CRD → established and discoverable → controller ready → Custom Resource → reconciled status.

CRD versus Custom Resource

A CRD registers a new API type with the Kubernetes API server; a CR is an instance of that type. For example, this CRD defines the Application kind:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: applications.argoproj.io

This object is a Custom Resource using the registered type:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: guestbook

The API server cannot create the second object until it recognizes the group, version, and kind from the first. CRDs are cluster-scoped; the resulting CRs can be namespaced or cluster-scoped according to spec.scope. See Kubernetes CRD documentation.

Why “installed” is not the same as “ready”

Registration and application readiness are separate checks:

Stage What it proves Typical check
CRD object exists The definition was stored kubectl get crd
CRD established and discoverable The API endpoint is available kubectl wait, then kubectl api-resources
Controller ready Software can watch and reconcile the type kubectl rollout status and controller health
CR healthy The desired state was achieved Resource status, events, and controller logs

Kubernetes notes that discovery can lag CRD creation by several seconds, so wait for the Established condition rather than inserting a fixed sleep. A CR can be stored before its controller exists, but it normally will have no status updates, dependent resources, or finalizer processing. The operator pattern combines a Custom Resource with a custom controller; installing only the CRD does not install that logic. See Kubernetes custom resources and controllers.

Apply raw manifests in explicit stages

  1. Confirm the cluster.
    kubectl config current-context
    kubectl cluster-info
  2. Apply the definitions.
    kubectl apply -f crds/
  3. Wait for registration.
    kubectl wait 
      --for=condition=Established 
      crd/applications.argoproj.io 
      --timeout=60s

    For several CRDs, repeat the wait for each name:

for crd in 
  applications.argoproj.io 
  applicationsets.argoproj.io 
  appprojects.argoproj.io
do
  kubectl wait --for=condition=Established "crd/${crd}" --timeout=60s
done
  1. Verify discovery.
    kubectl api-resources | grep -i application
    kubectl api-versions | grep argoproj.io
  2. Install the controller or operator and wait for it.
    kubectl apply -f operator/
    kubectl rollout status deployment/<controller-name> 
      -n <controller-namespace> --timeout=5m
  3. Apply Custom Resources.
    kubectl apply -f custom-resources/
  4. Check reconciliation.
    kubectl get <resource> -A
    kubectl describe <resource> <name> -n <namespace>
    kubectl get events -A --sort-by=.lastTimestamp

kubectl wait supports resource conditions and timeouts; it is more reliable than sleep 10. See kubectl wait reference.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Helm: use the crds/ convention deliberately

A chart that follows Helm’s convention places definitions in a top-level directory:

my-chart/
├── Chart.yaml
├── values.yaml
├── crds/
│   └── widgets.example.com.yaml
└── templates/
    └── widget.yaml

During helm install, Helm installs CRDs in crds/ before other chart resources when those CRDs are not already present:

helm install my-release ./my-chart 
  --namespace example --create-namespace

That mechanism has important boundaries:

  • Files in crds/ are not templated, and values cannot normally conditionally render them.
  • Existing CRDs are not automatically upgraded by Helm’s standard CRD handling.
  • CRDs are not automatically deleted when a release is uninstalled.
  • helm install --dry-run cannot fully validate a chart whose CRs depend on CRDs absent from the cluster, because discovery does not know the type yet.
  • --skip-crds disables installation and should be used only when another process owns the CRDs.
helm install my-release ./my-chart --skip-crds

Choose one owner—Helm, a dedicated CRD release, Argo CD, Flux, or cluster bootstrap—for each cluster-wide CRD. Do not let multiple tools mutate the same definition. Details are in Helm’s CRD best practices.

Helm upgrades and dry runs

Do not assume helm upgrade --install updates an existing CRD from crds/. A safer split is:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
kubectl apply -f crds/
helm upgrade --install my-release ./my-chart --skip-crds

Some charts expose their own CRD value. For example, the Argo chart documents chart-specific CRD settings; that option is not a generic Helm feature. Consult the chart’s versioned documentation, such as the Argo CD chart page.

For validation, establish the CRD first, render the chart, then use server-side dry run:

kubectl apply -f crds/
kubectl wait --for=condition=Established crd/widgets.example.com --timeout=60s
helm template my-release ./chart > rendered.yaml
kubectl apply --dry-run=server -f rendered.yaml
kubectl apply -f rendered.yaml

Argo CD: express dependency with sync waves

Argo CD orders resources by phase, wave, kind, and name. Lower waves run first, and negative waves are allowed. A practical layout is:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: widgets.example.com
  annotations:
    argocd.argoproj.io/sync-wave: "-2"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: widget-controller
  namespace: widget-system
  annotations:
    argocd.argoproj.io/sync-wave: "-1"
---
apiVersion: example.com/v1
kind: Widget
metadata:
  name: example-widget
  annotations:
    argocd.argoproj.io/sync-wave: "0"

Argo CD evaluates health as it advances. If the controller wave is unhealthy, later waves can remain blocked; a successful CRD wave alone does not prove that the application works. See Argo CD sync waves.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When Argo CD renders a Helm source, it installs chart CRDs by default when needed. Set skipCrds: true only if a separate Argo CD application or platform bootstrap owns them:

spec:
  source:
    helm:
      skipCrds: true

Keep ownership unambiguous, especially when using namespace-only Argo CD installations whose CRDs may be installed separately. See Argo CD Helm integration and Argo CD installation.

Flux: gate Helm releases with dependsOn

Flux Helm Controller can separate the CRD package from the controller package and make readiness explicit:

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: example-crds
  namespace: platform-system
spec:
  interval: 10m
  chart:
    spec:
      chart: example-crds
      sourceRef:
        kind: HelmRepository
        name: example
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: example-controller
  namespace: platform-system
spec:
  interval: 10m
  dependsOn:
    - name: example-crds
  chart:
    spec:
      chart: example-controller
      sourceRef:
        kind: HelmRepository
        name: example

The dependent release waits until the referenced release is ready. Flux also supports CRD policies such as Skip, Create, and CreateReplace; the documented default creates missing CRDs without replacing existing ones. Avoid circular dependsOn graphs, which cannot become ready. See Flux HelmRelease documentation and Flux Helm API reference.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Kustomize and other pipeline orchestrators

Kustomize transforms and renders manifests; it is not a universal dependency scheduler. Put CRDs in a separately applied base, then apply the operator and application bases. In CI/CD, Terraform, or Pulumi, model the same dependency graph explicitly and wait for establishment and controller rollout. A directory containing CRDs and CRs together may fail when a tool performs discovery or server-side validation before applying anything.

Safely handle existing CRDs and upgrades

Before changing an installed definition, inspect it:

kubectl get crd widgets.example.com -o yaml
kubectl describe crd widgets.example.com

Compare the API group, served and storage versions, scope, names and pluralization, OpenAPI schema, conversion strategy, printer columns, and webhook configuration. CRDs are persistent APIs, not disposable chart metadata. Kubernetes stores objects using the configured storage version; multi-version APIs may require conversion and data migration. Read the operator’s upgrade notes, back up representative CRs, apply vendor-provided CRD manifests, wait for Established, verify discovery and schema behavior, then upgrade the controller and validate statuses. If conversion webhooks are used, confirm their Service, certificates, and network path remain healthy. See Kubernetes CRD versioning.

Avoid kubectl replace --force on a live CRD unless the vendor explicitly requires it. Deleting a CRD can affect all of its Custom Resources across the cluster and may be destructive.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Troubleshoot common failures

Symptom Likely causes Checks
no matches for kind Missing CRD, wrong group/version/kind, wrong context, or discovery delay kubectl config current-context; kubectl get crd; kubectl api-resources | grep -i <kind>; kubectl api-versions | grep <group>
CRD exists but CR creation fails Endpoint not established, wrong served version or plural, terminating CRD, stale discovery, unavailable admission webhook kubectl get crd <name> -o yaml; kubectl get --raw /apis/<group>/<version>; inspect conditions and webhook events
CR accepted but does nothing Controller missing or crash-looping, RBAC denial, namespace/watch-scope mismatch, missing dependency kubectl get pods -n <operator-namespace>; kubectl logs deployment/<controller> -n <operator-namespace>; describe the CR and inspect events
Argo CD sync or comparison is blocked Incorrect waves, conflicting CRD owner, skipCrds mismatch, unhealthy earlier wave Review annotations, application boundaries, health status, and ownership
Works in one environment only CRD installed in a different cluster or context kubectl config current-context; kubectl cluster-info

Deployment checklist

  • Correct kubeconfig context and cluster confirmed.
  • CRD manifest applied by its single designated owner.
  • Established condition is true.
  • API discovery lists the expected resource and version.
  • Controller or operator is deployed, healthy, and authorized.
  • Custom Resource is applied only after those checks.
  • Resource status, events, and logs show reconciliation.
  • CRD upgrade ownership, backups, served/storage versions, and conversion-webhook checks are documented.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. Shenzhen desk3 min
    HONOR Expands Beyond Smartphones With Humanoid Robot RevealHONOR said it unveiled its first humanoid robot at MWC 2026 and named shopping assistance, workplace inspections, and supportive companionship as intended uses. Later Robotics D1 claims and a reported…
  2. Cupertino desk5 min
    Apple Unveils AirPods Max 2: The Upgrade That Should Have Happened Years AgoAirPods Max 2 adds H2-powered audio features and Apple claims up to 1.5× more effective ANC, but its design, Smart Case, and 20-hour battery rating are unchanged. Wired lossless audio…
  3. Cupertino desk4 min
    Apple’s OLED Touch MacBooks Are Coming—but the Dynamic Island Is the Real GambleApple has not announced an OLED touchscreen MacBook, but reports point to high-end models arriving in late 2026 or early 2027. The reported Mac Dynamic Island could be useful, but…
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.