> ## 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.

# Designing The WebApp Spec Schema

> Designing a minimal WebApp CRD spec with image and replicas and using operator defaults to reconcile Deployment Service and ConfigMap

Before you write controller code, define the shape of the custom resource that users will provide. That shape is the `spec` — think of it as a concise request form that declares the desired state. For this WebApp operator, the first API version keeps the `spec` intentionally small with just two fields.

The first field is `image`. It is a string that tells the Deployment which container image to run.

The second field is `replicas`. It is an `int32` (the conventional small integer type Kubernetes uses for replica counts). `replicas` tells the Deployment how many Pods should exist.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/euSqH_V95DCdREv4/images/Kubernetes-Operators/Building-The-WebApp-Operator-Core-Reconcile/Designing-The-WebApp-Spec-Schema/kubernetes-replicas-field-request-form.jpg?fit=max&auto=format&n=euSqH_V95DCdREv4&q=85&s=9ddb124a12608520a9f7d79985d841c9" alt="The image explains how to specify the number of pods to keep in Kubernetes using a &#x22;replicas&#x22; field, with a basic request form showing example fields for &#x22;image&#x22; and &#x22;replicas&#x22;. Additionally, it notes that Kubernetes uses the int32 type for replica counts." width="1920" height="1080" data-path="images/Kubernetes-Operators/Building-The-WebApp-Operator-Core-Reconcile/Designing-The-WebApp-Spec-Schema/kubernetes-replicas-field-request-form.jpg" />
</Frame>

Notice what is deliberately excluded from this first-version `spec`. There is no service type, no custom port, no nested configuration object, and no status field — those are left out so the initial API remains focused and easy to reason about.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/euSqH_V95DCdREv4/images/Kubernetes-Operators/Building-The-WebApp-Operator-Core-Reconcile/Designing-The-WebApp-Spec-Schema/deliberately-left-out-service-type-port-config.jpg?fit=max&auto=format&n=euSqH_V95DCdREv4&q=85&s=fd76606f26678fa58363a282b7121594" alt="The image features a list titled &#x22;Deliberately Left Out – For Now&#x22; with three items: Service type, Custom port, and Nested config object. Each item is accompanied by an icon." width="1920" height="1080" data-path="images/Kubernetes-Operators/Building-The-WebApp-Operator-Core-Reconcile/Designing-The-WebApp-Spec-Schema/deliberately-left-out-service-type-port-config.jpg" />
</Frame>

Even though the `spec` doesn't expose these options yet, the operator will still create a Service and a ConfigMap for the application. The operator uses clear, fixed defaults for those child resources so the reconciliation loop can be validated end to end without extra user configuration.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/euSqH_V95DCdREv4/images/Kubernetes-Operators/Building-The-WebApp-Operator-Core-Reconcile/Designing-The-WebApp-Spec-Schema/operator-service-configmap-diagram.jpg?fit=max&auto=format&n=euSqH_V95DCdREv4&q=85&s=7f13df7086e2ad4aa79a18b605911e4a" alt="The image shows a diagram with two labeled components: &#x22;Service&#x22; and &#x22;ConfigMap,&#x22; under the heading &#x22;The Operator Fills In Clear, Fixed Defaults.&#x22;" width="1920" height="1080" data-path="images/Kubernetes-Operators/Building-The-WebApp-Operator-Core-Reconcile/Designing-The-WebApp-Spec-Schema/operator-service-configmap-diagram.jpg" />
</Frame>

Concretely:

* Service default: `ClusterIP` on port `80`.
* ConfigMap default: a simple static welcome page.

This minimal-first-version approach keeps the user API small while letting you observe the reconcile loop working across Deployment, Service, and ConfigMap. You can add more `spec` fields later (for example `port`), but start small so the first reconciler remains readable and testable.

<Callout icon="lightbulb" color="#1CB2FE">
  Start with a narrow API surface. Small, opinionated defaults let you validate reconciliation behavior quickly; expand the `spec` only when you need to expose additional user-configurable options.
</Callout>

Example API sketches:

```yaml theme={null}
# First-version API
WebApp:
  spec:
    image: string
    replicas: int32
```

```yaml theme={null}
# API can grow later
WebApp:
  spec:
    image: string
    replicas: int32
    port: int32
```

The user writes a minimal desired state and the controller creates the child objects that make that state real.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/euSqH_V95DCdREv4/images/Kubernetes-Operators/Building-The-WebApp-Operator-Core-Reconcile/Designing-The-WebApp-Spec-Schema/reconciliation-process-flowchart-diagram.jpg?fit=max&auto=format&n=euSqH_V95DCdREv4&q=85&s=e06950f892bf576b1233294de2e30dc1" alt="The image illustrates a flowchart of a reconciliation process, where a user writes a small desired state, a controller creates children, and child objects make the state real." width="1920" height="1080" data-path="images/Kubernetes-Operators/Building-The-WebApp-Operator-Core-Reconcile/Designing-The-WebApp-Spec-Schema/reconciliation-process-flowchart-diagram.jpg" />
</Frame>

In Go, express this `spec` as fields on the `WebAppSpec` struct. The JSON struct tags determine the YAML/JSON field names users write in their manifests:

```go theme={null}
type WebAppSpec struct {
    Image    string `json:"image"`
    Replicas int32  `json:"replicas"`
}
```

Kubebuilder markers (Go comments processed by `controller-gen`) can add API-server behavior such as defaults and validation. Running `make manifests` converts those markers into the CRD OpenAPI schema that the API server enforces.

<Callout icon="warning" color="#FF6B6B">
  Remember to add kubebuilder validation and default markers if you expect defaults or input validation. Without them, the API server won't enforce constraints and invalid manifests could reach your controller.
</Callout>

Summary table — WebApp first-version contract:

| Field      | Type          | Meaning                                         | Default (controller-owned) |
| ---------- | ------------- | ----------------------------------------------- | -------------------------- |
| `image`    | `string`      | Container image to run in the Deployment        | n/a (required)             |
| `replicas` | `int32`       | Number of Pod replicas to maintain              | n/a (user-provided)        |
| Service    | (not in spec) | Operator creates a `ClusterIP` Service          | `ClusterIP` on port `80`   |
| ConfigMap  | (not in spec) | Operator creates a ConfigMap for a welcome page | Static welcome HTML        |

Next step: translate this design into controller and reconcile logic that instantiates the Deployment, Service, and ConfigMap. Implement the reconcile loop to:

1. Read the `WebApp` instance and its `spec`.
2. Construct desired child resources (Deployment, Service, ConfigMap) using the `spec` values and operator defaults.
3. Create or update child resources, set owner references, and ensure labels/selector consistency.
4. Requeue and reconcile until observed state matches desired state.

References:

* [Kubernetes Operators — Concepts](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/)
* [kubebuilder book](https://book.kubebuilder.io/)
* [controller-gen (kubebuilder marker docs)](https://book.kubebuilder.io/reference/markers.html)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/kubernetes-operators/module/ef5c1b44-311a-415f-8eeb-8a460e759cfe/lesson/806a4312-3ca5-4ce7-aa47-7336e21a1324" />
</CardGroup>
