Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Helm Plugin (helm/v2-alpha)

The helm/v2-alpha plugin generates Helm charts from your project’s kustomize output, letting you distribute your operator as either a bundle or a Helm chart.

The plugin dynamically builds charts from make build-installer output and preserves your customizations like environment variables, labels, annotations, and security contexts.

Why use Helm

By default, Kubebuilder creates a bundle of manifests:

make build-installer IMG=<registry>/<project-name:tag>

Users install it with:

kubectl apply -f https://raw.githubusercontent.com/<org>/project-v4/<tag-or-branch>/dist/install.yaml

Many users prefer Helm for packaging and upgrades. This plugin converts dist/install.yaml into a Helm chart that mirrors your project.

Features

  • Generates charts from kustomize output, not boilerplate
  • Preserves environment variables, labels, annotations, and patches
  • Organizes templates to match your config/ directory layout
  • Includes only configurable parameters in values.yaml
  • Never overwrites Chart.yaml; preserves values.yaml, NOTES.txt, _helpers.tpl, .helmignore, test-chart.yml, network-policy/allow-metrics-traffic.yaml, and network-policy/allow-webhook-traffic.yaml unless you use --force
  • Adds default ServiceMonitor and NetworkPolicy templates when kustomize output does not provide them
  • Places custom resources in templates/extras/ with Helm templating

Usage

Basic workflow

Create a project and build the installer bundle:

kubebuilder init
make build-installer IMG=<registry>/<project:tag>

Generate the Helm chart from kustomize output:

kubebuilder edit --plugins=helm/v2-alpha

To regenerate preserved files (except Chart.yaml), use --force:

kubebuilder edit --plugins=helm/v2-alpha --force

Advanced options

Use a custom manifests file:

kubebuilder edit --plugins=helm/v2-alpha --manifests=manifests/custom-install.yaml

Write chart to a custom output directory:

kubebuilder edit --plugins=helm/v2-alpha --output-dir=charts

Combine custom manifests and output directory:

kubebuilder edit --plugins=helm/v2-alpha \
  --manifests=manifests/install.yaml \
  --output-dir=helm-charts

Chart structure

The plugin generates a chart layout that mirrors your config/ directory:

<output-dir>/chart/
├── Chart.yaml
├── values.yaml
├── .helmignore
└── templates/
    ├── NOTES.txt
    ├── _helpers.tpl
    ├── rbac/                    # Individual RBAC files (examples)
    │   ├── controller-manager.yaml
    │   ├── leader-election-role.yaml
    │   ├── leader-election-rolebinding.yaml
    │   ├── manager-role.yaml
    │   ├── manager-rolebinding.yaml
    │   ├── metrics-auth-role.yaml
    │   ├── metrics-auth-rolebinding.yaml
    │   ├── metrics-reader.yaml
    │   └── ...
    ├── crd/                     # Individual CRD files (examples)
    │   ├── busyboxes.example.com.testproject.org.yaml
    │   └── ...
    ├── cert-manager/
    │   ├── metrics-certs.yaml
    │   ├── selfsigned-issuer.yaml
    │   └── serving-cert.yaml
    ├── manager/
    │   └── manager.yaml
    ├── metrics/
    │   └── controller-manager-metrics-service.yaml
    ├── webhook/
    │   ├── validating-webhook-configuration.yaml
    │   └── webhook-service.yaml
    ├── monitoring/
    │   └── servicemonitor.yaml
    ├── network-policy/
    │   ├── allow-metrics-traffic.yaml
    │   └── allow-webhook-traffic.yaml  # If webhooks are configured
    └── extras/                  # Custom resources (if any)
        ├── my-service.yaml
        └── my-config.yaml

Values configuration

The generated values.yaml provides configuration options extracted from your actual deployment. Namespace creation is not managed by the chart; use Helm’s --namespace and --create-namespace flags when installing.

How values are formatted

Values are uncommented when:

  • Extracted from kustomize source manifests
  • Standard Helm fields (replicas, image, resource names)

Values stay commented when:

  • Optional Kubernetes features not in use (imagePullSecrets, priorityClassName)
  • Advanced configuration not needed for basic usage (topology spread, pod disruption budget)
  • User customization fields (name overrides, custom labels)

Example:

## String to partially override chart.fullname template (will maintain the release name)
##
# nameOverride: ""

## String to fully override chart.fullname template
##
# fullnameOverride: ""

## Configure the controller manager deployment
##
manager:
  ## Set to false to skip manager installation
  ##
  enabled: true

  replicas: 1

  image:
    repository: controller
    ## Image tag (defaults to Chart.appVersion if not set)
    ##
    # tag: ""
    pullPolicy: IfNotPresent

  ## Arguments
  ##
  args:
    - --leader-elect

  ## Health probes.
  ## The manager serves the liveness (/healthz) and readiness (/readyz) endpoints on this port.
  ##
  healthProbe:
    # Health probe server port
    port: 8081

  ## Image pull secrets
  ##
  # imagePullSecrets:
  #   - name: myregistrykey

  ## Pod-level security settings
  ##
  podSecurityContext:
    runAsNonRoot: true
    seccompProfile:
      type: RuntimeDefault

  ## Container-level security settings
  ##
  securityContext:
    allowPrivilegeEscalation: false
    capabilities:
      drop:
      - ALL
    readOnlyRootFilesystem: true

  ## Resource limits and requests
  ##
  resources:
    limits:
      cpu: 500m
      memory: 128Mi
    requests:
      cpu: 10m
      memory: 64Mi

  ## Manager pod's affinity
  ##
  affinity: {}

  ## Manager pod's node selector
  ##
  nodeSelector: {}

  ## Manager pod's tolerations
  ##
  tolerations: []

  ## Deployment strategy
  ##
  # strategy:
  #   type: RollingUpdate
  #   rollingUpdate:
  #     maxSurge: 25%
  #     maxUnavailable: 25%

  ## Priority class name
  ##
  # priorityClassName: ""

  ## Topology spread constraints
  ##
  # topologySpreadConstraints: []

  ## Termination grace period seconds
  ##
  terminationGracePeriodSeconds: 10

  ## Custom Deployment labels
  ##
  # labels: {}

  ## Custom Deployment annotations
  ##
  # annotations: {}

  ## Custom Pod labels and annotations
  ##
  # pod:
  #   labels: {}
  #   annotations: {}

## RBAC configuration
##
rbac:
  ## RBAC resource scope
  ## - false (default): ClusterRole/ClusterRoleBinding (all namespaces)
  ## - true: Role/RoleBinding (release namespace only)
  ##
  namespaced: false

  ## Helper roles for CRD management (admin/editor/viewer)
  ##
  helpers:
    ## Install convenience admin/editor/viewer roles for CRDs
    ##
    enabled: false

## ServiceAccount configuration
##
serviceAccount:
  # Install default ServiceAccount provided
  enabled: true

  ## Existing ServiceAccount name (required when enabled=false)
  ## Set to "default" to use the namespace default ServiceAccount
  ## Note: When enabled=true, respects nameOverride/fullnameOverride
  ##
  # name: ""

  ## Custom ServiceAccount annotations
  ##
  # annotations: {}

  ## Custom ServiceAccount labels
  ##
  # labels: {}

## Custom Resource Definitions
##
crd:
  # Install CRDs with the chart
  enabled: true
  # Keep CRDs when uninstalling
  keep: true

## Controller metrics endpoint.
## Enable to expose /metrics endpoint
##
metrics:
  enabled: true
  # Metrics server port
  port: 8443
  # Enable secure metrics: HTTPS with certs/auth (true) or HTTP (false).
  # Note: Metrics authn/authz needs ClusterRole access.
  secure: true

## Cert-manager integration for TLS certificates.
## Required for webhook certificates and metrics endpoint certificates.
##
certManager:
  enabled: false

## Prometheus ServiceMonitor for metrics scraping.
## Requires prometheus-operator to be installed in the cluster.
##
prometheus:
  enabled: false

## Network policies for controlling traffic flow.
## Enable to restrict ingress to the controller manager.
##
networkPolicy:
  enabled: false

Installation

The plugin adds Helm targets to your Makefile:

make helm-deploy IMG=<registry>/<project:tag>
make helm-status

Install manually with all features enabled:

helm install my-release ./dist/chart --namespace my-project-system --create-namespace

Install only CRDs and RBAC:

helm install my-release ./dist/chart --set manager.enabled=false --set webhook.enabled=false

Install without webhooks:

helm install my-release ./dist/chart --set webhook.enabled=false --set certManager.enabled=false

Install with NetworkPolicy resources:

helm install my-release ./dist/chart --set networkPolicy.enabled=true

Extra volumes

Add volumes and volume mounts to the manager deployment beyond webhook and metrics certificates.

Volumes in your kustomize configuration (config/manager/manager.yaml or patches) are written to the chart template. When the manager has extra volumes, values.yaml includes manager.extraVolumes and manager.extraVolumeMounts fields. Use these to add more volumes at install time.

Webhook and metrics certificates (webhook-certs, metrics-certs) are managed separately and controlled by certManager.enabled and (for metrics TLS) metrics.enabled + metrics.secure.

Metrics configuration

metrics.port

Set metrics.port to change the port used by the metrics endpoint. The chart applies the same value to the manager --metrics-bind-address argument, the metrics Service port and targetPort, and the metrics NetworkPolicy.

For example, install the chart with the metrics endpoint on port 8444:

helm install my-operator ./dist/chart --set metrics.port=8444

The default is 8443, detected from your project configuration.

metrics.secure

Control transport security and authentication for the metrics endpoint (default: true).

When true:

  • Uses HTTPS with TLS certificates (when certManager.enabled=true)
  • Creates metrics-auth-role ClusterRole for authentication
  • ServiceMonitor uses HTTPS

When false:

  • Uses HTTP without authentication
  • No TLS certificates
  • ServiceMonitor uses HTTP

Webhook port configuration

Set webhook.port to change the port used by the manager webhook server. The chart applies the same value to the manager argument, container port, webhook service, and webhook NetworkPolicy.

For example, install the chart with the webhook server on port 9444:

helm install my-operator ./dist/chart --set webhook.port=9444

The default is 9443, detected from your project configuration.

Health probe port configuration

Set manager.healthProbe.port to change the port where the manager serves its health probes. The liveness (/healthz) and readiness (/readyz) endpoints bind to this port. The chart applies the same value to the --health-probe-bind-address argument, the health container port, and the httpGet port of both probes.

For example, install the chart with the health probes on port 8082:

helm install my-operator ./dist/chart --set manager.healthProbe.port=8082

The default is 8081, detected from your project configuration.

Passing args for the manager

Use manager.args in values.yaml to pass extra flags to the manager container that the chart does not expose as dedicated values. The chart renders each entry through Helm’s tpl function, evaluated against the chart’s root context, so an arg can reference other values, release information, or chart template functions instead of only a static string.

For example, set the leader election namespace from the release namespace and add a plain flag:

manager:
  args:
  - --leader-election-namespace={{ .Release.Namespace }}
  - --leader-elect

Helm evaluates {{ .Release.Namespace }} at render time, so the manager container receives --leader-election-namespace=<release-namespace>. The --leader-elect flag has no template syntax, so it renders unchanged.

NetworkPolicy configuration

Set networkPolicy.enabled: true to install NetworkPolicy resources for the manager pod.

When the kustomize output includes NetworkPolicy resources, the plugin converts them into chart templates and sets networkPolicy.enabled: true. When no NetworkPolicy resources are present in the kustomize output, the plugin generates default templates for metrics traffic, and also for webhook traffic when webhooks are detected in the provided kustomize input files.

Custom labels and annotations

Add custom labels and annotations using manager.labels, manager.annotations, manager.pod.labels, and manager.pod.annotations. Duplicate keys from kustomize are filtered automatically.

ServiceAccount configuration

Set serviceAccount.enabled: true (default) to create a ServiceAccount. Set serviceAccount.enabled: false to use an existing one:

serviceAccount:
  enabled: false
  name: my-existing-sa

The chart ships with the toggle enabled:

serviceAccount:
  enabled: true

Helm merges this default into any values file that omits the section, so omitting it keeps creating the account.

When serviceAccount.enabled: false, serviceAccount.name is required and the chart fails to render without it. Set name: default explicitly to use the namespace default ServiceAccount.

The resolved name is used consistently in the Deployment and in all RBAC bindings, for both cluster-scoped and namespaced RBAC modes:

serviceAccount.enabledserviceAccount.nameServiceAccount createdName used (Deployment + RBAC bindings)
true (default)anyYesGenerated (<fullname>-controller-manager, name is ignored)
falsesetNoThe provided name
falsedefaultNoNamespace default ServiceAccount
falseunsetNoRender fails with a clear error

The generated name is built from the chart fullname, typically <release>-<chart>, plus the -controller-manager suffix, truncated to the 63-character Kubernetes limit. It respects nameOverride and fullnameOverride.

Add annotations for cloud provider integrations:

serviceAccount:
  enabled: true
  annotations:
    iam.gke.io/gcp-service-account: my-operator@project.iam.gserviceaccount.com

External ServiceAccount names are used as-is and ignore nameOverride or fullnameOverride.

RBAC configuration

rbac.namespaced

Set the scope of RBAC permissions:

  • false (default): ClusterRole and ClusterRoleBinding for all namespaces
  • true: Role and RoleBinding for release namespace only

rbac.roleNamespaces

When your kustomize output includes Roles and RoleBindings for specific namespaces (other than the manager namespace), the plugin automatically detects them and creates roleNamespaces entries.

Add namespace-specific RBAC markers to your controller:

// +kubebuilder:rbac:groups=apps,namespace=infrastructure,resources=deployments,verbs=get;list;watch
// +kubebuilder:rbac:groups="",namespace=users,resources=secrets,verbs=get;list;watch

Run make manifests to generate the RBAC manifests, then run the plugin. The generated values.yaml includes:

rbac:
  namespaced: false

  ## Namespace configuration for Roles deployed to namespaces different from the manager namespace
  ## Keys are resource name suffixes (without project prefix)
  ##
  roleNamespaces:
    # RBAC resource manager-role-infrastructure deploys to namespace infrastructure
    "manager-role-infrastructure": "infrastructure"
    # RBAC resource manager-rolebinding-infrastructure deploys to namespace infrastructure
    "manager-rolebinding-infrastructure": "infrastructure"
    # RBAC resource manager-role-users deploys to namespace users
    "manager-role-users": "users"
    # RBAC resource manager-rolebinding-users deploys to namespace users
    "manager-rolebinding-users": "users"

  helpers:
    enabled: false

Override namespaces at deployment using a values file:

# custom-values.yaml
rbac:
  roleNamespaces:
    "manager-role-infrastructure": "prod-infra"
    "manager-rolebinding-infrastructure": "prod-infra"
    "manager-role-users": "prod-users"
    "manager-rolebinding-users": "prod-users"

Install with custom namespaces:

helm install my-operator ./dist/chart -f custom-values.yaml

Or use --set:

helm install my-operator ./dist/chart \
  --set 'rbac.roleNamespaces[manager-role-infrastructure]=prod-infra' \
  --set 'rbac.roleNamespaces[manager-role-users]=prod-users'

Flags

FlagDescription
–manifestsPath to YAML file containing Kubernetes manifests (default: dist/install.yaml)
–output-dir stringOutput directory for chart (default: dist)
–forceRegenerates preserved files except Chart.yaml (values.yaml, NOTES.txt, _helpers.tpl, .helmignore, test-chart.yml, network-policy/allow-metrics-traffic.yaml, network-policy/allow-webhook-traffic.yaml)