Project Setup with kubebuilder

Table of Contents


Introduction

kubebuilder is the scaffolding CLI maintained by the Kubernetes SIGs team. It generates a working Go project β€” go.mod, main.go, Makefile, RBAC manifests, and CRD generation tooling β€” from a few commands. The goal isn't to hide complexity; it's to give you a consistent, idiomatic project structure so you can focus on reconcile logic rather than boilerplate.

This article walks through initializing appstack-operator, understanding every file that gets generated, and running the controller locally against a kind cluster.


Installing the Toolchain

Go

kubebuilder

kubebuilder doesn't have a brew formula. Download it directly from the releases page or use the command above.

kind (local Kubernetes)

controller-gen

controller-gen is the code generation tool that reads Go struct markers and outputs CRD YAML and DeepCopy methods. kubebuilder installs it automatically via the Makefile, but you can install it manually:


Initializing the Project

The --domain flag sets the API group domain. Your CRDs will live under apps.htunn.io/v1alpha1. The --repo flag sets the Go module name in go.mod.

Expected output:


Understanding the Generated Structure

Makefile Targets

The generated Makefile is the interface you'll use every day:

The PROJECT File

kubebuilder writes a PROJECT file that tracks what APIs and controllers you've scaffolded:

When you run kubebuilder create api, it appends to this file. It's also used by kubebuilder to validate compatibility when you add new resources.


Scaffolding the API and Controller

kubebuilder will prompt:

Answer y to both. This generates:

The PROJECT file is updated:


Understanding main.go

The generated cmd/main.go is the entry point. It's worth reading in full before touching any controller logic:

Key things to understand in this file:

The scheme: A scheme maps Go types (AppStack) to Kubernetes API group/version/kind strings. You register both built-in types and your custom types in init(). Every API call goes through the scheme.

The Manager: The ctrl.Manager starts all your registered controllers, manages the client cache, serves the metrics endpoint, and handles leader election. You start it with mgr.Start(), which blocks until a signal is received.

SetupWithManager: Each controller calls this to register itself with the manager. This is where you configure watches (covered in the controller article).


Running the Operator Locally

Before writing any reconcile logic, verify the scaffolded project builds and runs:

Install the CRD

Expected:

Run the Controller

This runs go run ./cmd/main.go with the current kubeconfig. The controller connects to your kind cluster, registers event watches, and begins its goroutines. You should see output like:

Apply a Test Resource

While make run is running, open a second terminal:

The controller will call Reconcile(). In the logs you'll see:

Nothing happens yet because the reconcile body is empty in the scaffold. That logic is what the next articles build.


The Development Loop

The day-to-day loop when developing an operator:

Keeping make run running while you modify resources in another terminal is the most efficient development flow. You don't need to rebuild or redeploy between changes β€” just restart make run when you change Go code.

Useful Debug Commands


Next: CRD Design and Go API Types β†’

Last updated