Skip to main content
When you first open an operator project it can feel like a warehouse full of boxes: Go files, YAML manifests, generated code, RBAC rules, tests, webhooks, and deployment manifests. It’s easy to lose sight of the core components that actually implement the operator. This guide strips that complexity down to the essentials so you can immediately see where the operator lives and what each piece does.
The image shows the title "A Warehouse Full of Boxes" with filenames like main.go, types.go, and deployment.yaml scattered around, and a central question: "Where's the operator?".
Conceptually, think of an operator as a vending machine.
  • The CRD (CustomResourceDefinition) is the front panel: the set of buttons and the display where users make requests.
  • The controller is the internal machinery: motors, coils, and control logic that translate button presses into actions.
Buttons without machinery do nothing, and machinery without buttons cannot receive orders. An operator requires both the CRD (the interface) and the controller (the implementation).
The image illustrates the concept of an operator as two components glued together: an interface labeled "CRD" for asking and machinery labeled "controller" for doing the work.
What the CRD does
  • A CRD teaches Kubernetes a new kind of API object (for example, WebApp).
  • After the CRD is installed, you can kubectl create, kubectl get, and kubectl delete those custom resources just like built-in types.
Example: a WebApp resource introduced into the webapp.codecloud.com/v1 API group will appear as a first-class Kubernetes resource.
The image illustrates how a Custom Resource Definition (CRD) registers a new kind, "webapps," into the Kubernetes API.
WebApp resources typically follow the common Kubernetes pattern of separating desired state from observed state:
  • spec — the desired state the user requests.
  • status — the observed state the controller reports back.
Example WebApp manifest:
Quick reference: common WebApp fields What the controller does The controller is the running program that watches WebApp objects and executes a reconcile loop to make reality match the spec. Concretely, reconciliation follows this pattern:
  1. Read the spec (what the user asked for).
  2. Inspect the current cluster state (what exists now).
  3. Create, update, or delete child resources to move the cluster toward the desired state.
  4. Update the status field to reflect what was actually achieved.
The image depicts a process involving a controller running a reconcile() function, which includes reading what is asked for and inspecting existing conditions, as part of an operator API workflow.
Typical child resources created by a WebApp controller
  • Deployment — defines the Pod template and replica count.
  • Pod — the running container instance(s).
  • Service — stable network identity and load-balancing for Pods.
  • ConfigMap — configuration data as key/value pairs.
Linking parent and child objects The controller sets an ownerReference on child objects that points back to the parent WebApp. With proper ownerReferences, Kubernetes’ garbage collector automatically deletes child objects when the parent is removed. Around the reconcile loop: supporting pieces in an operator project
  • Manager — boots and hosts one or more controllers and shared resources.
  • Cache / Informers — a local, read-efficient view of cluster objects used by controllers.
  • Client — API client for reading and mutating cluster state.
  • RBAC — the set of Kubernetes permissions that determine what actions the controller can perform.
Keep the vending machine mental model: the CRD is the front interface, the controller is the machinery that performs work, child Kubernetes objects are the items produced, and status reports the result back to users and tooling.
The image illustrates a vending machine metaphor for a process, with elements labeled as "CRD" (the front), "Controller" (the machinery inside), and "Child objects" (what comes out), explaining their functions.
A few practical tips and resources
  • Always verify your controller’s RBAC rules include permissions for resources it reads and writes (CRs, Deployments, Pods, Services, ConfigMaps, etc.). Missing RBAC permissions are a common source of runtime failures.
  • Use ownerReferences so Kubernetes can automatically garbage-collect resources you create.
  • Prefer the controller-runtime manager and informers to avoid writing low-level watch code.
If your controller lacks appropriate RBAC permissions, reconciliation will fail silently or produce events. Always audit the Role/ClusterRole and RoleBinding/ClusterRoleBinding in your operator manifests.
Links and references Once you internalize this shape—the CRD as the interface and the controller as the reconciler—the larger project layout and generated files become much easier to navigate and reason about.

Watch Video