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; preservesvalues.yaml,NOTES.txt,_helpers.tpl,.helmignore, andtest-chart.ymlunless 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-roleClusterRole 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 namespacestrue: 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
| Flag | Description |
|---|---|
| –manifests | Path to YAML file containing Kubernetes manifests (default: dist/install.yaml) |
| –output-dir string | Output directory for chart (default: dist) |
| –force | Regenerates preserved files except Chart.yaml (values.yaml, NOTES.txt, _helpers.tpl, .helmignore, test-chart.yml) |