> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Project Structure Walkthrough

> A walkthrough of a Kubebuilder scaffolded operator project explaining files, directories, and where to add APIs, controllers, and deployment manifests

You just ran `kubebuilder init`. When you open the generated folder it can feel like Kubebuilder dumped a lot of files at once.

Think of this project as a labeled workbench: Kubebuilder has already placed the tools and drawers before you build the operator. You don't need to memorize every file — just know which drawer to open for each job.

<Callout icon="lightbulb" color="#1CB2FE">
  Treat this layout as a simple map: knowing where startup code, API types, reconcile logic, deployment manifests, and helper targets live is enough to get productive.
</Callout>

Start at the root.

PROJECT is the label on this workbench. It records the domain, repo path, project name, and every API you add later. Kubebuilder regenerates it as needed, so you generally should not hand-edit it.

<Callout icon="warning" color="#FF6B6B">
  The `PROJECT` file is code-generated. Avoid manual edits unless you fully understand the implications—let Kubebuilder and plugins manage this file.
</Callout>

A typical generated `PROJECT` file looks like:

```yaml theme={null}
# Code generated by tool. DO NOT EDIT.
# This file is used to track the info used to scaffold your project
# and allow the plugins properly work.
# More info: https://book.kubebuilder.io/reference/project-config.html
cliVersion: 4.14.0
domain: kodekloud.com
layout:
  - go.kubebuilder.io/v4
projectName: webapp-operator
repo: github.com/kodekloud/webapp-operator
version: "3"
```

Next to it, `go.mod` is the parts list for a Go project. It pins dependencies such as controller-runtime and the Kubernetes client libraries.

The `Makefile` is the row of buttons on the bench. Targets like `make manifests`, `make generate`, `make run`, and `make docker-build` run the same workflow the same way every time. Below is an excerpt showing common variables and setup that many targets depend on:

```makefile theme={null}
# Image URL to use all building/pushing image targets
IMG ?= controller:latest
# YEAR defines the year value used for substituting the YEAR placeholder in the build
YEAR ?= $(shell date +%Y)

# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (, $(shell go env GOBIN))
GOBIN=$(shell go env GOPATH)/bin
else
GOBIN=$(shell go env GOBIN)
endif

# CONTAINER_TOOL defines the container tool to be used for building images.
# Be aware that the target commands are only tested with Docker which is
# scaffolded by default. However, you might want to replace it to use other
# tools. (i.e. podman)
CONTAINER_TOOL ?= docker

# Setting SHELL to bash allows bash commands to be executed by recipes.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec
```

`Dockerfile` is the packaging station: one stage builds the Go binary, and a smaller image runs it. This multi-stage example is the common pattern Kubebuilder projects use (slightly cleaned for clarity):

```dockerfile theme={null}
# Build the manager binary
FROM golang:1.25 AS builder
ARG TARGETOS
ARG TARGETARCH

WORKDIR /workspace

# Copy the Go Modules manifests
COPY go.mod go.sum ./

# Cache deps before building and copying source so that we don't need to re-download
# and so that source changes don't invalidate our downloaded layer
RUN go mod download

# Copy the Go source (relies on .dockerignore to filter)
COPY . .

# Build
# Allow target architecture/OS to be provided via build args. Default to linux.
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager .
```

The code in `cmd/main.go` is the operator's power switch and the entry point for the controller-runtime manager. The manager owns the shared Kubernetes client, the cache, metrics and health probes, leader election, and controller registration. A minimal example showing webhook server setup looks like:

```go theme={null}
package main

import (
    "sigs.k8s.io/controller-runtime/pkg/webhook"
    // other imports...
)

func main() {
    // Assume tlsOpts, webhookCertPath, webhookCertName, webhookCertKey, setupLog are defined elsewhere.

    // Initial webhook TLS options
    webhookServerOptions := webhook.Options{
        TLSOpts: tlsOpts,
    }

    if len(webhookCertPath) > 0 {
        setupLog.Info("Initializing webhook certificate watcher", "webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName)
        webhookServerOptions.CertDir = webhookCertPath
        webhookServerOptions.CertName = webhookCertName
        webhookServerOptions.KeyName = webhookCertKey
    }

    webhookServer := webhook.NewServer(webhookServerOptions)
    _ = webhookServer // used later when registering webhooks or starting the manager
}
```

After you scaffold an API with `kubebuilder create api`, the generated controllers will be registered from this startup code.

The `config/` directory is the deployment drawer. It holds Kustomize overlays and base manifests so you can compose deployable YAML without duplicating shared pieces. For example, a `config/default/kustomization.yaml` often ties resources together and can include namespace, namePrefix, labels, and other resource references:

```yaml theme={null}
# Adds namespace to all resources.
namespace: webapp-operator-system
# Value of this field is prepended to the
# names of all resources, e.g. a deployment named
# "wordpress" becomes "alices-wordpress".
# Note that it should also match with the prefix (text before '-' ) of the namespace.
namePrefix: webapp-operator-
# Labels to add to all resources and selectors.
labels:
  # includeSelectors: true
  # pairs:
  #   someName: someValue

resources:
  # - ../crd
  # - ../rbac
  # - ../manager
  # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix in
  # crd/kustomization.yaml
```

* `config/manager` contains the controller Deployment/Pod spec.
* `config/rbac` contains Role/ClusterRole and RoleBinding/ClusterRoleBinding yaml.
* `config/crd/` will hold generated CRD YAMLs after `make manifests`.
* `config/samples/` stores example custom resources you can apply to test controllers.

Kubebuilder also scaffolds helper files like `hack/boilerplate.go.txt` to append license headers to generated Go files.

A sample RBAC rule in `config/rbac` looks like:

```yaml theme={null}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/name: webapp-operator
    app.kubernetes.io/managed-by: kustomize
  name: manager-role
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "watch"]
```

`test/` is where end-to-end or integration tests live. `.golangci.yml` configures the linter used by `make lint`. A typical configuration that demonstrates plugin usage is:

```yaml theme={null}
# This file configures golangci-lint with module plugins.
# When you run 'make lint', it will automatically build a custom golangci-lint binary
# with all the plugins listed below.
#
# See: https://golangci-lint.run/plugins/module-plugins/
version: v2.11.4
plugins:
  # logcheck validates structured logging calls and parameters (e.g., balanced key/value pairs)
  - module: "sigs.k8s.io/logtools"
    import: "sigs.k8s.io/logtools/logcheck/gclplugin"
    version: latest
```

These support tools are important to know where they live, but you don't need to memorize every setting. Focus on the files you’ll edit most often.

Now notice what is not here yet: there is no `api/` directory and no `controllers/` directory. That is expected: those folders appear after you run `kubebuilder create api`.

* `api/` will hold the webapp Go types (API shapes).
* `controllers/` will hold the reconciler, which is the code that reacts when a webapp changes.

Quick directory map

| Path           | Purpose                                     | Typical contents                              |
| -------------- | ------------------------------------------- | --------------------------------------------- |
| `cmd/`         | Startup code / manager entry point          | `main.go`, webhook and manager wiring         |
| `api/`         | API type definitions (after scaffolding)    | `v1alpha1` types, `zz_generated.deepcopy.go`  |
| `controllers/` | Reconcile logic (after scaffolding)         | Controller structs, `*_controller.go`         |
| `config/`      | Deployment manifests and Kustomize overlays | CRDs, RBAC, manager deployment, samples       |
| `hack/`        | Helper scripts and boilerplate              | `boilerplate.go.txt`                          |
| `test/`        | Integration / e2e tests                     | Test harness, e2e specs                       |
| root files     | Project metadata and build tools            | `PROJECT`, `go.mod`, `Makefile`, `Dockerfile` |

Keep this map in your head: startup code in `cmd/`, API shapes in `api/`, reconcile logic in `controllers/`, deployment YAML in `config/`, and repeatable commands in the `Makefile`. Once that map is clear, the scaffold stops feeling like a wall of files and starts feeling like a workbench with labeled drawers.

Use `kubebuilder create api` to scaffold your first API and controller.

Links and references

* [Kubebuilder Book — Project Layout](https://book.kubebuilder.io/reference/project-config.html)
* [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime)
* [Kustomize](https://kustomize.io/)
* [golangci-lint — Plugins](https://golangci-lint.run/plugins/module-plugins/)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/kubernetes-operators/module/20a4ec01-fde8-466d-83c7-2f74f6def1f0/lesson/3a7a64b0-9757-43f8-b3dc-cbd16825ad4b" />
</CardGroup>
