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, and test-chart.yml unless you use --force
  • 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
    └── 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

  ## 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
    ##
    enable: false

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

  ## Existing ServiceAccount name (only when enable=false)
  ## Note: When enable=true, respects nameOverride/fullnameOverride
  ##
  # name: ""

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

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

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

## Controller metrics endpoint.
## Enable to expose /metrics endpoint
##
metrics:
  enable: 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:
  enable: false

## Prometheus ServiceMonitor for metrics scraping.
## Requires prometheus-operator to be installed in the cluster.
##
prometheus:
  enable: 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.enable=false

Install without webhooks:

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

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.enable and metrics.enable.

Metrics configuration

metrics.secure

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

When true:

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

When false:

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

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.enable: true (default) to create a ServiceAccount. Set serviceAccount.enable: false to use an existing one.

Add annotations for cloud provider integrations:

serviceAccount:
  enable: 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:
    enable: 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)