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

# Visual Builder

> Generate Kubernetes manifests from a form — no YAML required.

## Overview

The **Visual Builder** is a form-driven manifest generator built into the Niro dashboard. It lets you describe a workload in plain terms and generates the corresponding Kubernetes YAML (Deployment, Service, Ingress, HPA, PVC, etc.).

You don't need to know Kubernetes YAML syntax to use the builder. The generated manifests are valid, production-ready Kubernetes resources that follow Niro's [policy rules](/features/policies).

## What the builder generates

Given your inputs, the builder produces a multi-document YAML file containing:

| Resource                        | When generated                                         |
| ------------------------------- | ------------------------------------------------------ |
| `Namespace`                     | Always (if the namespace doesn't exist)                |
| `ConfigMap`                     | If you specify config values                           |
| `Secret`                        | If you specify secret values or use a private registry |
| `Deployment` (or `StatefulSet`) | Always — the main workload                             |
| `Service`                       | If you enable networking                               |
| `Ingress`                       | If you enable HTTP routing                             |
| `HorizontalPodAutoscaler`       | If you enable auto-scaling                             |
| `PersistentVolumeClaim`         | If you enable storage                                  |

The builder also injects `imagePullSecrets` automatically when you select a registry credential.

## Builder sections

### Workload

The core of every deployment.

**Image**

Enter the full image reference: `registry/repo:tag`. For private images, select a [registry credential](/guides/private-registries) and the pull secret is injected automatically.

The builder flags the `latest-image-tag` policy rule if you use a mutable tag. Use an explicit version tag like `v1.2.3` or a digest `sha256:...` for reproducibility.

**Container configuration**

* **Name** — the Deployment and Service name (must be a valid DNS label)
* **Command** — overrides `ENTRYPOINT` (equivalent to `command:` in pod spec)
* **Args** — overrides `CMD` (equivalent to `args:` in pod spec)
* **Ports** — container port(s) to expose. Used to generate the Service and Ingress

**Environment variables**

Add key-value pairs that are injected as `env` entries in the container spec. Values are stored as literals. Use [Secrets](#secrets) if you need to reference Kubernetes Secrets or ConfigMaps.

**Resources**

| Field           | Description                       | Example           |
| --------------- | --------------------------------- | ----------------- |
| CPU requests    | Minimum guaranteed CPU            | `100m` (0.1 core) |
| CPU limits      | Maximum CPU the container can use | `500m`            |
| Memory requests | Minimum guaranteed memory         | `128Mi`           |
| Memory limits   | Maximum memory                    | `512Mi`           |

<Tip>
  Missing requests and limits triggers the `missing-requests` and `missing-limits` policy rules. Set at least requests to ensure the Kubernetes scheduler can place your pod correctly.
</Tip>

**Health probes**

Configure liveness and/or readiness probes:

| Probe type | Configuration                          |
| ---------- | -------------------------------------- |
| **HTTP**   | Path + port + initial delay + period   |
| **TCP**    | Port + initial delay + period          |
| **Exec**   | Command array + initial delay + period |

Missing probes trigger the `missing-probes` policy rule. A readiness probe prevents traffic from reaching a pod that isn't ready; a liveness probe restarts a pod that is stuck.

### Networking

Enable a **Service** to expose the workload inside the cluster (or externally):

* **ClusterIP** — accessible only within the cluster (default)
* **NodePort** — accessible on each node's IP at a fixed port
* **LoadBalancer** — requests an external load balancer from the cloud provider

Enable an **Ingress** to route HTTP(S) traffic by hostname and path. Requires a running Ingress controller (Traefik is bundled with k3s).

### Scaling

Set a fixed replica count, or enable a **HorizontalPodAutoscaler**:

| Field                     | Description                                           |
| ------------------------- | ----------------------------------------------------- |
| Replicas                  | Static replica count (default: 1)                     |
| Min replicas              | HPA lower bound                                       |
| Max replicas              | HPA upper bound                                       |
| Target CPU utilization    | Percentage of requested CPU that triggers scale-up    |
| Target memory utilization | Percentage of requested memory that triggers scale-up |

<Warning>
  A single replica is flagged by the `single-replica` policy rule, which escalates to high severity on production clusters. Add replicas and a [Pod Disruption Budget](https://kubernetes.io/docs/tasks/run-application/configure-pdb/) for production workloads.
</Warning>

### Storage

Add a **PersistentVolumeClaim** for stateful workloads:

| Field         | Description                                                                         |
| ------------- | ----------------------------------------------------------------------------------- |
| Size          | e.g. `5Gi`, `20Gi`                                                                  |
| Access mode   | `ReadWriteOnce` (single node) or `ReadWriteMany` (requires shared storage provider) |
| Mount path    | Where inside the container the volume is mounted                                    |
| Storage class | Leave empty to use the cluster's default `StorageClass`                             |

### Secrets

Reference existing Kubernetes resources as environment variables:

**envFrom** — inject all keys from a ConfigMap or Secret:

```yaml theme={"theme":{"light":"github-light","dark":"vesper"}}
# Generated pod spec fragment
envFrom:
  - secretRef:
      name: my-app-secrets
  - configMapRef:
      name: my-app-config
```

**secretKeyRef / configMapKeyRef** — inject a specific key with a custom env var name:

```yaml theme={"theme":{"light":"github-light","dark":"vesper"}}
# Generated pod spec fragment
env:
  - name: DATABASE_URL
    valueFrom:
      secretKeyRef:
        name: db-credentials
        key: url
```

<Note>
  The referenced Secret or ConfigMap must exist in the same namespace as the app. You can create and manage them from the cluster **Config** panel before deploying the app.
</Note>

## Policy findings

As you fill in the builder form, Niro evaluates your configuration against the [policy rules](/features/policies) in real time and shows inline findings:

* Missing resource limits or requests → `missing-requests` / `missing-limits`
* Missing health probes → `missing-probes`
* Mutable image tag → `latest-image-tag`
* Single replica (on a production cluster) → `single-replica`
* Known-private registry without credentials → `public-image-no-credential`
* Privileged container or allow privilege escalation → `privileged-container`

All findings are advisory. You can deploy despite findings, but they'll continue to surface at runtime and in PR reviews.

## Editing an existing deployment

Open the deployment in the **Apps** list and click **Edit**. The builder reopens with the form populated from the last saved values.

After editing:

* **Re-apply** to push the changes directly to the cluster
* **Commit to repo** to open a new PR with the updated manifests

<Note>
  Niro stores the builder form data (not just the generated YAML), so reopening and editing always shows the same fields you filled in — not a reverse-engineered representation of the YAML.
</Note>

## Importing existing YAML

If you have existing Kubernetes manifests, use **Import** from the Apps page to scan a repo folder and convert them to Niro-managed deployments. Imported deployments start as read-only; you can convert them to builder-managed deployments to enable visual editing.

## Related

* [Deploy an App](/guides/deploy-app) — step-by-step guide to deploying with the builder
* [Manage Secrets and ConfigMaps](/guides/secrets-configmaps) — create configuration resources for apps
* [Policies & Standards](/features/policies) — rules evaluated on every builder submit
* [Private Registries](/guides/private-registries) — pull from private registries
* [Set Up GitOps](/guides/gitops) — commit builder output to a Git repo
