Every KCNA candidate learns that a Deployment is an object, that kubectl apply sends it to the API server, and that a controller makes the cluster match it. Far fewer stop to ask the question the exam quietly tests: where did the Deployment type come from, and can I add my own? The answer is yes, and the mechanism — custom resources, CustomResourceDefinitions (CRDs), custom controllers, and the Operator pattern — is the reason the cloud native ecosystem is built on Kubernetes rather than next to it. Argo CD, Flux, cert-manager, KEDA, Istio, the Gateway API, Prometheus Operator and Knative are, at the API level, all the same thing: new object types plus the controllers that act on them.
The existing KCNA guides on this site cover the pieces around this topic. The Kubernetes object model guide explains API groups, versions and kubectl verbs, the architecture fundamentals guide explains the reconciliation loop inside kube-controller-manager, and the application delivery guide mentions Operators in one paragraph. None of them explains the extension mechanism itself. This article does, at KCNA altitude: enough to recognise every phrasing the exam uses, with the real manifests and commands so the concepts stick, and without the hands-on authoring depth that belongs to the CKAD CRD and Operator guide.
Where This Sits in the KCNA Curriculum
The KCNA curriculum was refreshed in late 2025. The current domains are Kubernetes Fundamentals (44%), Container Orchestration (28%), Cloud Native Application Delivery (16%) and Cloud Native Architecture (12%). Custom resources are not a named competency; they are a thread that runs through three of them:
| Domain | Competency | How custom resources show up |
|---|---|---|
| Kubernetes Fundamentals | Kubernetes Core Concepts | What the API server serves, how new kinds are registered, kubectl api-resources |
| Kubernetes Fundamentals | Administration | Installing extensions, RBAC for new resource types, what deleting a CRD destroys |
| Cloud Native Application Delivery | Application Delivery | Argo CD Application, Flux Kustomization and HelmRelease, Helm charts that ship CRDs |
| Cloud Native Architecture | Cloud Native Ecosystem and Principles | Operators as the way the ecosystem encodes operational knowledge; CNCF projects as CRDs plus controllers |
The exam does not ask you to write a CRD. It asks whether you understand what one is, what it is not, and why the pattern matters. Typical stems: “Which Kubernetes feature allows an administrator to add a new resource type without modifying the API server?”, “A custom resource was created but nothing happened. What is missing?”, “Which component watches custom resources and takes action?”, “What is an Operator?”.
Built-in Resources, Custom Resources and the API Server
A resource in Kubernetes is an endpoint in the API that stores a collection of objects of one kind. The built-in pods resource stores Pod objects at /api/v1/namespaces/NAMESPACE/pods. Deployments live under the apps group at /apis/apps/v1/namespaces/NAMESPACE/deployments. Every resource, built-in or not, follows the same REST conventions: create, read, update, delete, list, watch, and patch, with the object stored in etcd and served over HTTPS by kube-apiserver.
A custom resource is an extension of that API — a new endpoint and a new kind that is not part of a default Kubernetes installation. Two properties make it powerful:
- It is registered dynamically into a running cluster. No API server rebuild, no restart, no cluster upgrade. A cluster administrator applies a manifest and seconds later
kubectl getworks for the new kind. - It is served and stored by the same API server as built-in objects. The custom resource is a first-class citizen: same authentication, same RBAC, same audit log, same
kubectl, same watch stream that controllers use.
The important mental separation for the exam is between the definition (the CRD, which teaches the API server a new kind) and the instances (the custom resources, which are objects of that kind). The CRD is itself a Kubernetes object, of kind CustomResourceDefinition in the apiextensions.k8s.io/v1 group, and it is cluster-scoped — there is no such thing as a namespaced CRD, even though the resources it defines are usually namespaced.
Anatomy of a CustomResourceDefinition
The Kubernetes documentation uses a CronTab example. Read it once with the comments and you will recognise every CRD you ever meet:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
# name must be <plural>.<group>
name: crontabs.stable.example.com
spec:
# group name used in the REST path: /apis/<group>/<version>
group: stable.example.com
versions:
- name: v1
# each version can be enabled or disabled independently
served: true
# exactly one version must be the storage version
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
cronSpec:
type: string
image:
type: string
replicas:
type: integer
# Namespaced or Cluster
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
shortNames:
- ct
Applying that single manifest makes the API server serve a new endpoint:
/apis/stable.example.com/v1/namespaces/*/crontabs/...
From that moment a user with the right RBAC can create an instance. Notice that the instance uses the group and version from the CRD as its apiVersion and the kind from the CRD’s names block — exactly like apps/v1 and Deployment:
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
name: my-new-cron-object
spec:
cronSpec: "* * * * */5"
image: my-awesome-cron-image
kubectl apply -f my-crontab.yaml
kubectl get crontab # singular, plural and shortName all work
kubectl get ct -o yaml
kubectl describe crontab my-new-cron-object
The parts of the definition that the exam can reasonably expect you to recognise:
| Field | What it controls | Exam cue |
|---|---|---|
metadata.name | Must be plural.group | A question showing a name that does not match the group is testing this rule |
spec.group | The API group, visible in apiVersion and in kubectl api-resources | Groups are usually a DNS name you control |
spec.versions[] | One or more versions, each served true/false, exactly one storage: true | ”Which version is written to etcd?” — the storage version |
spec.scope | Namespaced or Cluster | The CRD object itself is always cluster-scoped |
spec.names | plural, singular, kind, shortNames, optional categories | categories: [all] makes kubectl get all include the type |
schema.openAPIV3Schema | Structural schema used for validation, pruning and kubectl explain | Mandatory in apiextensions.k8s.io/v1 |
Two administrative facts are tested often because they are destructive:
- Deleting a CRD deletes every custom object of that kind in the cluster. The REST endpoint disappears and the stored objects go with it. Recreating the CRD later starts from zero. This is why Helm, by design, does not delete CRDs when a release is uninstalled.
- Deleting a namespace deletes the namespaced custom objects inside it, exactly as it deletes Pods and ConfigMaps.
What a Custom Resource Gets for Free
The reason CRDs are the default extension mechanism is the list of API-server features a new kind inherits without a single line of code. The Kubernetes documentation lists them; the ones worth knowing for the KCNA are:
| Feature | Meaning for a custom resource |
|---|---|
| CRUD and list | kubectl create/get/apply/delete, plus every client library that speaks the Kubernetes API |
| Watch | Clients and controllers receive a stream of changes instead of polling |
| Discovery | kubectl api-resources and kubectl explain know the new type; dashboards can display it |
| Validation | OpenAPI v3 schema checks on every write; unknown fields are pruned; CEL validation rules (x-kubernetes-validations, stable since v1.29) express cross-field constraints such as self.minReplicas <= self.replicas |
| Defaulting | default: values in the schema are applied on read and write |
| Authentication, authorization, audit | Same as built-in objects; RBAC rules name the group and resource, for example apiGroups: ["stable.example.com"], resources: ["crontabs"] |
| Finalizers | A controller can block deletion until external cleanup finishes; the first delete only sets metadata.deletionTimestamp |
status subresource | Separates .spec (desired) from .status (observed); writes to /status ignore spec changes and metadata.generation only increments on spec changes |
scale subresource | Exposes /scale so kubectl scale and the HorizontalPodAutoscaler work on the custom kind |
| Additional printer columns | kubectl get can show fields from the object, not just NAME and AGE |
| Selectable fields | --field-selector spec.color=blue on custom resources (stable since v1.32) |
| Multiple versions | Serve v1beta1 and v1 at once with a conversion strategy |
| Labels and annotations | Standard metadata, usable in selectors and by tooling |
One consequence that the exam likes: RBAC does not automatically grant anyone access to a new resource type. Only cluster-admin and roles written with wildcards see it. Projects that ship CRDs therefore ship ClusterRoles alongside them, and the common pattern of aggregating those into the built-in view, edit and admin roles uses the rbac.authorization.k8s.io/aggregate-to-* labels.
A Custom Resource Alone Does Nothing
This is the single most important idea in the topic and the one the exam phrases in the most ways. A CRD teaches the API server to store and serve structured data of a new kind. It does not teach anything to act on that data. If you create the CronTab above on a fresh cluster, the object sits in etcd, kubectl get crontab lists it, and nothing else ever happens. There is no cron job, no Pod, no image pull.
Behaviour comes from a custom controller: a program, usually running in a Pod as an ordinary Deployment, that watches the custom resource through the API server and reconciles the real world toward the declared .spec, then writes what it observed into .status. Pairing a custom resource with a custom controller is what turns a data store into a declarative API — you say what you want, the controller works out how, continuously, forever. That is exactly the model the architecture fundamentals guide describes for the built-in controllers in kube-controller-manager; a custom controller simply does the same job for a kind that Kubernetes did not ship with.
The reconciliation loop, stripped to its essentials:
loop forever:
event = watch(CronTab objects) # create / update / delete / resync
desired = read(object.spec)
actual = observe the cluster (Pods, Jobs, external systems)
if actual != desired:
create / update / delete things until they match
write(object.status, what was observed)
Three properties follow from that loop and all three appear as exam distractors:
- The controller is level-based, not edge-based: it compares full desired and actual state every time, so a missed event does not matter — the next resync corrects it.
- The controller runs outside the control plane, as an API client. Custom controllers are not compiled into kube-controller-manager, and they can be deployed and upgraded independently of the cluster version.
- The controller needs RBAC of its own, normally via a ServiceAccount bound to a Role or ClusterRole that can get, list and watch the custom resource and create whatever it manages.
The Operator Pattern
An Operator is a custom resource plus a custom controller, built to manage a specific application the way a skilled human operator would. The Kubernetes documentation defines Operators as software extensions that use custom resources to manage applications and their components, following the control-loop principle. The motivation is to capture what the person who runs a service knows — how to deploy it, how to back it up, how to upgrade it, how to react when it breaks — and encode that knowledge as a controller.
The documentation’s example is a SampleDB Operator. Walk through what it automates and you have the vocabulary for every “what does an Operator do?” question:
- On create of a
SampleDBobject: create PersistentVolumeClaims for durable storage, a StatefulSet to run the database, and a Job to initialise it. - On delete: take a final snapshot, then remove the StatefulSet and volumes.
- Backups: for each
SampleDB, schedule Pods that connect to the database and take backups, reading credentials from a Secret. - Upgrades: detect that a database runs an old version and create Jobs that upgrade it.
- Day-2 extras: publish a Service, simulate failures for resilience testing, elect a leader for a distributed application.
Using an Operator is the same as using any Kubernetes object — you never talk to the controller directly:
kubectl get SampleDB # find configured databases
kubectl edit SampleDB/example-database # change settings; the Operator reconciles
kubectl delete SampleDB/example-database # the Operator snapshots and cleans up
The Operator Framework community describes five capability levels, and the KCNA occasionally borrows the vocabulary, so recognise the progression:
| Level | Name | What the Operator handles |
|---|---|---|
| 1 | Basic Install | Automated provisioning and configuration from the custom resource |
| 2 | Seamless Upgrades | Upgrading the Operator and the application it manages with minimal user effort |
| 3 | Full Lifecycle | Backup, restore and resilient operation of the managed application |
| 4 | Deep Insights | Metrics, alerts and events that show what the managed application is doing |
| 5 | Auto Pilot | Automatic scaling, self-healing and tuning |
Two clarifications prevent the classic mistakes:
- Helm is not an Operator. A Helm chart templates manifests and installs them; after
helm installnothing is watching. An Operator keeps reconciling after installation. Charts and Operators cooperate all the time — the chart is how you install the Operator and its CRDs. The application delivery guide covers the Helm and Kustomize side. - A Deployment is managed by a built-in controller, not an Operator. “Operator” is reserved for custom-resource-driven controllers that encode application-specific knowledge.
Operators can be written in any language that can call the Kubernetes API. The documentation lists frameworks including kubebuilder, the Operator Framework / Operator SDK, Kopf (Python), kube-rs (Rust), Java Operator SDK, KubeOps (.NET), Metacontroller and shell-operator. You will not be asked to pick one; you may be asked to recognise that an Operator is “a controller for a custom resource” and that OperatorHub.io is where ready-made Operators are published.
The Ecosystem You Already Use Is Built on CRDs
The quickest way to make this topic concrete is to notice that most CNCF projects covered elsewhere in the KCNA curriculum are CRDs plus controllers. When a question describes one of these tools, the answer “it extends the Kubernetes API with custom resources and a controller” is almost always correct:
| Project | Custom resources it adds | What its controller does |
|---|---|---|
| Argo CD | Application, ApplicationSet, AppProject | Reconciles the cluster to a Git repository (GitOps pull model) |
| Flux | GitRepository, Kustomization, HelmRelease | A toolkit of controllers that apply Git-sourced manifests and charts |
| cert-manager | Certificate, Issuer, ClusterIssuer | Requests, renews and stores TLS certificates as Secrets |
| Prometheus Operator | Prometheus, ServiceMonitor, PrometheusRule, Alertmanager | Deploys and configures Prometheus from declarative objects |
| KEDA | ScaledObject, ScaledJob, TriggerAuthentication | Scales workloads on external event sources, including to zero — see the serverless guide |
| Knative Serving | Service, Route, Configuration, Revision in the serving.knative.dev group | Runs request-driven, scale-to-zero containers |
| Istio | VirtualService, DestinationRule, Gateway, PeerAuthentication | Programs the data-plane proxies — see the service mesh guide |
| Gateway API | GatewayClass, Gateway, HTTPRoute | The successor to Ingress ships as CRDs implemented by many controllers |
| Cilium | CiliumNetworkPolicy, CiliumClusterwideNetworkPolicy | Extends NetworkPolicy with L7 rules |
| Crossplane | Provider-specific kinds for cloud resources, plus Composition | Manages infrastructure outside the cluster from Kubernetes objects |
| Kyverno | ClusterPolicy, Policy | Validates, mutates and generates resources as admission policy |
Notice the pattern in the names: kind is CamelCase, the group is a DNS name owned by the project, and kubectl get works on all of them. That is what “Kubernetes as a universal control plane” means in practice.
CRDs vs the Aggregation Layer
CRDs are one of two ways to add an API to Kubernetes. The other is API aggregation: you write and run your own API server (an extension API server, typically in Pods in the cluster), register it with an APIService object that claims a URL path such as /apis/metrics.k8s.io/v1beta1, and kube-apiserver proxies requests for that path to your server. The best-known example is the metrics server that powers kubectl top and the HorizontalPodAutoscaler.
The documentation’s comparison, condensed to the rows that matter for recognition:
| Aspect | CRD | Aggregated API |
|---|---|---|
| Programming required | No — a YAML definition; the controller can be in any language | Yes — build a binary and a container image for the API server itself |
| Extra service to run | None; kube-apiserver serves and stores the objects | Yes, an additional server that can fail and must be kept up to date |
| Storage | etcd, via kube-apiserver | Anything you choose — a time-series database, encrypted storage, an external system |
| Validation and defaulting | OpenAPI v3 schema, CEL rules, admission webhooks | Arbitrary code |
| Subresources | status and scale only | Any — for example logs or exec |
| Protocol Buffers and strategic merge patch | Not supported | Supported |
| Latency requirement | None special | Discovery requests must round-trip from kube-apiserver in five seconds or less |
The guidance is simple: CRDs are easier, aggregated APIs are more flexible. A handful of fields used within one organisation or a small project is a CRD. Custom storage, non-CRUD operations or very high request rates point to aggregation — or to not using the Kubernetes API at all.
The documentation adds a warning that turns up as a judgement question: do not use custom resources as a store for application, end-user or monitoring data. Custom resources consume etcd like ConfigMaps, and large or high-churn data overloads the API server. A custom resource should describe configuration and desired state — a relatively small number of relatively small objects, read and written mostly by humans and controllers.
ConfigMap or custom resource?
Because both hold configuration, the exam can ask which one fits. The documentation’s rule of thumb:
| Prefer a ConfigMap when | Prefer a custom resource when |
|---|---|
An existing, well-documented file format already exists (mysql.cnf, pom.xml) | You want kubectl and client-library support as a first-class type |
| The whole configuration fits in one key and a program in a Pod reads it | You want automation that watches the object and creates, updates or deletes other objects |
| Consumers want a file or environment variable, not the API | You want the .spec / .status / .metadata conventions |
| You want a Deployment rollout when the file changes | Humans and controllers will read and write it through the API |
The ConfigMaps and Secrets guide covers the ConfigMap side in depth.
Admission: Where Policy Meets Custom Resources
Custom resources flow through the same request pipeline as everything else — authentication, authorization, then admission control — so the extension points there apply to them too. The KCNA expects recognition of the mechanisms, not configuration:
- Mutating and validating admission webhooks call an external HTTPS service you run; it can change the object (mutating) or reject it (validating). Operators often ship a validating webhook so that a malformed custom resource is refused at
kubectl applytime rather than failing silently in the controller. Webhooks add a network dependency and therefore a failure point. - ValidatingAdmissionPolicy is the in-process, declarative alternative: CEL expressions evaluated inside kube-apiserver with no external service. It is a stable API in current Kubernetes.
- MutatingAdmissionPolicy does the same for mutations and is stable since Kubernetes v1.36, in the
admissionregistration.k8s.io/v1group.
The distinction the exam draws: a CRD’s schema validates shape (types, required fields, ranges, CEL rules within one object), while admission can enforce policy that depends on cluster context — who is making the request, what namespace, what other objects exist. Both can apply to the same custom resource.
Beyond the API: The Other Extension Points
Custom resources are the extension point candidates meet first, but the Kubernetes documentation lists a wider set, and the KCNA occasionally asks you to place a technology on this map:
| Extension point | Mechanism | Examples |
|---|---|---|
| Client | kubectl plugins (binaries named kubectl-foo on your PATH) | krew-distributed plugins |
| API access | Authentication webhooks, authorization webhooks, admission webhooks and policies | OIDC login, policy engines |
| API resources | CRDs and the aggregation layer | Everything in the table above, metrics server |
| Scheduling | Scheduler plugins and multiple scheduler profiles | Custom placement logic |
| Controllers | Custom controllers and Operators | Argo CD, cert-manager |
| Network | CNI plugins executed by the kubelet | Cilium, Calico |
| Storage | CSI drivers | Cloud block storage drivers |
| Devices | Device plugins and Dynamic Resource Allocation | GPUs, FPGAs |
Three patterns describe how all of these plug in: controllers (API clients that reconcile), webhooks (Kubernetes calls out to a service you run), and binary plugins (a node or client executes a program — CNI, CSI, kubectl plugins). Read a question about “how X integrates with Kubernetes” and you can usually name the pattern before you name the product.
Discovering Custom Resources on Any Cluster
The KCNA is multiple-choice, but the commands make the concepts stick and they are precisely what you will use on the day you meet an unfamiliar cluster. The CKAD guide turns this into a timed workflow; here is the short form:
# Every resource type the API server knows, built-in and custom, with group and shortnames
kubectl api-resources
# Only the custom definitions
kubectl get crd
# Which API groups and versions are served (custom groups appear alongside apps, batch, ...)
kubectl api-versions
# The schema of a custom kind, straight from the CRD's OpenAPI schema
kubectl explain certificates.cert-manager.io
kubectl explain certificates.cert-manager.io.spec --recursive
# Instances, with the extra printer columns the CRD defines
kubectl get certificates -A
kubectl get certificate my-cert -o yaml # look at .status to see what the controller observed
# Is anything reconciling it? Find the controller
kubectl get deploy -A | grep -i cert-manager
If kubectl get shows the object but .status never changes and no Pods, Secrets or other children appear, the controller is missing, crashed, or lacks RBAC — the three answers to every “I created a custom resource and nothing happened” question.
How the KCNA Phrases It: Scenario Drill
| Scenario in the question | Answer the exam wants |
|---|---|
| ”Add a new resource type to the API without modifying Kubernetes source” | Create a CustomResourceDefinition |
| ”A CRD was applied and an object created, but no Pods were started” | A controller (Operator) that watches the custom resource has not been installed |
| ”Software that encodes operational knowledge — backups, upgrades, failover — for an application on Kubernetes” | An Operator |
| ”What does an Operator consist of?” | A custom resource (CRD) plus a custom controller running the reconciliation loop |
| ”Which component serves and stores custom resources?“ | kube-apiserver, with objects in etcd |
| ”Is a CRD namespaced?” | No — CRDs are cluster-scoped; the custom resources they define may be namespaced or cluster-scoped |
| ”What happens to custom objects when their CRD is deleted?” | They are all deleted |
| ”Need custom storage or non-CRUD operations for an extension API” | API aggregation with an extension API server and an APIService |
”Which kubectl command lists all resource types including custom ones?” | kubectl api-resources |
| ”Argo CD / cert-manager / KEDA are examples of …” | Kubernetes extensions built on custom resources and controllers |
| ”Reject a custom resource that violates organisational policy at creation time” | Admission control — a validating webhook or a ValidatingAdmissionPolicy |
| ”Does Helm replace Operators?” | No — Helm installs; an Operator keeps reconciling after installation |
Common Mistakes
- Treating the CRD as the thing that does work. The CRD is a schema registration. Work is done by a controller. Keep the two words apart in your head and the exam cannot trip you.
- Confusing the CRD object with the custom resource instances.
kubectl get crdlists definitions;kubectl get crontabslists instances. - Assuming RBAC grants access automatically. New resource types are invisible to
view,editandadminuntil aggregated ClusterRoles are added. - Thinking Operators are a Kubernetes component. They are ordinary workloads — usually Deployments — that act as API clients, installed by the cluster administrator.
- Equating Helm charts with Operators. Install-time templating versus run-time reconciliation.
- Storing data in custom resources. They are for desired state, not for application or metrics data; that belongs in a database or a time-series store.
- Believing API aggregation is the common path. It is the rare path. CRDs cover the overwhelming majority of real extensions, including most CNCF projects.
Practising This Topic
Custom resources are a recognition topic, and recognition is built by seeing the same idea in a dozen phrasings — “extend the API”, “new kind”, “Operator”, “controller watches”, “nothing happened”, “api-resources”. The KCNA practice sets in the Sailor.sh KCNA mock exam bundle spread these across Kubernetes Fundamentals, Application Delivery and Cloud Native Architecture questions the way the real exam does, with explanations that name the mechanism behind each answer. Use them after you can reproduce the CronTab example and the reconciliation loop from memory, and treat a miss on any row of the scenario drill above as a signal to reread that section.
For broader preparation, the KCNA exam guide covers format and domains, the study guide sequences the topics, and the practice questions show the question style across every domain.
Frequently Asked Questions
What is the difference between a CRD, a custom resource and an Operator?
A CRD is the definition that registers a new kind with the API server. A custom resource is an instance of that kind — an object you create with kubectl apply. An Operator is a custom controller, usually deployed as a Pod, that watches those custom resources and makes the cluster match their .spec, encoding application-specific operational knowledge. CRD plus controller equals Operator.
Does the KCNA require me to write a CRD or an Operator?
No. The KCNA is multiple-choice and tests concepts. You should be able to read a CRD and a custom resource manifest, explain what each field does at a high level, and reason about what happens — or does not happen — when one is applied. Hands-on authoring is CKAD and CKA territory.
Are custom resources stored in etcd?
Yes. kube-apiserver stores custom resources in etcd exactly like built-in objects, using the version marked storage: true in the CRD. That is also why the documentation warns against using them for large or high-churn data.
Can I use RBAC with custom resources?
Yes, and you must. Custom resources use the same authentication, authorization and audit logging as built-in resources, but existing roles do not automatically cover new types. Projects ship ClusterRoles for their kinds and often aggregate them into view, edit and admin.
When should I use API aggregation instead of a CRD?
When you need something a CRD cannot provide — custom storage, subresources beyond status and scale, Protocol Buffers, or non-CRUD semantics — and you are prepared to build, run and maintain your own API server. For everything else, use a CRD.
Is every Kubernetes controller an Operator?
No. Built-in controllers in kube-controller-manager manage built-in kinds. “Operator” refers to a custom controller for a custom resource that captures how to run a specific application. All Operators are controllers; not all controllers are Operators.
Why does nothing happen after I create a custom resource?
Because a CRD only stores data. Either the controller for that kind is not installed, it is not running, or it lacks the RBAC permissions to watch the resource and create what it manages. Check for the controller’s Deployment and its logs.
Conclusion
Custom resources are the mechanism that turned Kubernetes from a container scheduler into a universal control plane. A CRD teaches the API server a new kind and hands it every API feature for free — storage, watch, discovery, validation, RBAC, finalizers, subresources — but it is inert until a controller reconciles it. Package the two together with real operational knowledge and you have an Operator; look at Argo CD, Flux, cert-manager, KEDA, Istio and the Gateway API and you see the same pattern everywhere. For the KCNA, keep three sentences ready: a CRD defines, a custom resource declares, a controller acts. Add the CRD-versus-aggregation comparison, the deletion rule, the RBAC caveat and the admission layer, and this corner of the Kubernetes Fundamentals domain becomes reliable marks.