-->
When working with Helm, you’ll often need to go beyond the default manifests provided by a chart. There are multiple strategies to extend and customize charts depending on whether they come from a traditional Helm repository or from an OCI registry (stored as artifacts).
helm repo add grafana https://grafana.github.io/helm-charts
helm template grafana/grafana
helm template oci://ghcr.io/grafana/helm-charts/grafana
The customization methods are the same in both cases.
Every chart exposes default values through values.yaml
. Always start here.
helm show values grafana/grafana > example.yaml
Also visible here.
Create a new file my-values.yaml
:
namespaceOverride: monitoring
ingress:
enabled: true
hosts:
- grafana.example.local
persistence:
enabled: true
size: 5Gi
extraEnv:
- name: GF_SECURITY_ALLOW_EMBEDDING
value: "true"
Every variable added in this file overrides the default config provided with the Helm chart.
helm template grafana grafana/grafana -f my-values.yaml
You should now be able to see the impacted changes in the output of the previous command.
If you need resources not covered by the chart (e.g., NetworkPolicy, ServiceMonitor):
The Grafana chart includes extraObjects
:
extraObjects:
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: grafana-allow-same-namespace
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: grafana
ingress:
- from:
- podSelector:
matchLabels:
access-to-grafana: "true"
policyTypes:
- Ingress
Instead of forking Grafana’s chart, you create your own chart that declares Grafana as a dependency. This lets you:
Step 1. Create a new chart
helm create my-grafana
cd my-grafana
When you create your own chart (wrapper), you’ll always end up with the same minimal structure:
my-grafana/
├── Chart.yaml
├── templates
└── values.yaml
If you run the command helm template .
(here .
being the current folder path), you should now see the example files from the templates
folder rendered.
In our case you can empty the templates folder so we can start with a clean chart.
You can then update the Chart.yaml
file to add the dependency section at the end of the file:
dependencies:
- name: grafana
version: 10.0.0
repository: https://grafana.github.io/helm-charts
You can then run the command:
helm dependency update
A new folder charts/
appears alongside a Chart.lock
file:
my-grafana/
├── Chart.lock
├── charts
├── Chart.yaml
├── templates
└── values.yaml
If you run again the command helm template .
, you should now see the entire Grafana chart rendered.
If you create a new file templates/network-policy.yaml
:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: grafana-allow-same-namespace
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: grafana
ingress:
- from:
- podSelector:
matchLabels:
access-to-grafana: "true"
policyTypes:
- Ingress
The helm template
command should now include this extra manifest in the output.
You can also override the entire values.yaml
content and add the overrides we specified in step one to impact the Grafana manifests:
namespaceOverride: monitoring
ingress:
enabled: true
hosts:
- grafana.example.local
persistence:
enabled: true
size: 5Gi
extraEnv:
- name: GF_SECURITY_ALLOW_EMBEDDING
value: "true"
Helm charts use Go templating to replace variables from values.yaml
and chart metadata into Kubernetes manifests. This is what makes charts reusable and configurable across environments.
As an example, let’s take the custom NetworkPolicy we added earlier. Instead of hardcoding grafana
, we can update our file templates/network-policy.yaml
to use Helm template variables:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: {{ .Release.Name }}-allow-same-namespace
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: {{ .Chart.Name }}
ingress:
- from:
- podSelector:
matchLabels:
access-to-{{ .Chart.Name }}: "true"
policyTypes:
- Ingress
{{ .Release.Name }}
: replaced by the name you give the release when running helm install
.{{ .Chart.Name }}
: replaced by the chart’s name.Any values defined in the values.yaml
can be accessed with {{ .Values.example }}
inside a templated file.
This is much more efficient than hardcoding values. By leveraging templating, you can make your extra manifests dynamic and environment-aware, just like the original chart’s templates.