Headlamp Helm Chart
Headlamp is an easy-to-use and extensible Kubernetes web UI that provides:
- 🚀 Modern, fast, and responsive interface
- 🔒 OIDC authentication support
- 🔌 Plugin system for extensibility
- 🎯 Real-time cluster state updates
Prerequisites
- Kubernetes 1.21+
- Helm 3.x
- Cluster admin access for initial setup
Quick Start
Add the Headlamp repository and install the chart:
$ helm repo add headlamp https://kubernetes-sigs.github.io/headlamp/
$ helm repo update
$ helm install my-headlamp headlamp/headlamp --namespace kube-system
Access Headlamp:
$ kubectl port-forward -n kube-system svc/my-headlamp 8080:80
Then open http://localhost:8080 in your browser.
Installation
Basic Installation
$ helm install my-headlamp headlamp/headlamp --namespace kube-system
Installation with OIDC
$ helm install my-headlamp headlamp/headlamp \
--namespace kube-system \
--set config.oidc.clientID=your-client-id \
--set config.oidc.clientSecret=your-client-secret \
--set config.oidc.issuerURL=https://your-issuer-url
Installation with Ingress
$ helm install my-headlamp headlamp/headlamp \
--namespace kube-system \
--set ingress.enabled=true \
--set ingress.hosts[0].host=headlamp.example.com \
--set ingress.hosts[0].paths[0].path=/
Configuration
Core Parameters
| Key | Type | Default | Description |
|---|---|---|---|
| replicaCount | int | 1 |
Number of desired pods |
| image.registry | string | "ghcr.io" |
Container image registry |
| image.repository | string | "headlamp-k8s/headlamp" |
Container image name |
| image.tag | string | "" |
Container image tag (defaults to Chart appVersion) |
| image.pullPolicy | string | "IfNotPresent" |
Image pull policy |
Application Configuration
| Key | Type | Default | Description |
|---|---|---|---|
| config.inCluster | bool | true |
Run Headlamp in-cluster |
| config.baseURL | string | "" |
Base URL path for Headlamp UI |
| config.sessionTTL | int | 86400 |
The time in seconds for the internal session to remain valid (Default: 86400/24h, Min: 1 , Max: 31536000/1yr) |
| config.pluginsDir | string | "/headlamp/plugins" |
Directory to load Headlamp plugins from |
| config.enableHelm | bool | false |
Enable Helm operations like install, upgrade and uninstall of Helm charts |
| config.extraArgs | array | [] |
Additional arguments for Headlamp server |
| config.tlsCertPath | string | "" |
Certificate for serving TLS |
| config.tlsKeyPath | string | "" |
Key for serving TLS |
OIDC Configuration
| Key | Type | Default | Description |
|---|---|---|---|
| config.oidc.clientID | string | "" |
OIDC client ID |
| config.oidc.clientSecret | string | "" |
OIDC client secret |
| config.oidc.issuerURL | string | "" |
OIDC issuer URL |
| config.oidc.scopes | string | "" |
OIDC scopes to be used |
| config.oidc.usePKCE | bool | false |
Use PKCE (Proof Key for Code Exchange) for enhanced security in OIDC flow |
| config.oidc.secret.create | bool | true |
Create OIDC secret using provided values |
| config.oidc.secret.name | string | "oidc" |
Name of the OIDC secret |
| config.oidc.externalSecret.enabled | bool | false |
Enable using external secret for OIDC |
| config.oidc.externalSecret.name | string | "" |
Name of external OIDC secret |
| config.oidc.meUserInfoURL | string | "" |
URL to fetch additional user info for the /me endpoint. Useful for providers like oauth2-proxy. |
There are three ways to configure OIDC:
- Using direct configuration:
config:
oidc:
clientID: "your-client-id"
clientSecret: "your-client-secret"
issuerURL: "https://your-issuer"
scopes: "openid profile email"
meUserInfoURL: "https://headlamp.example.com/oauth2/userinfo"
- Using automatic secret creation:
config:
oidc:
secret:
create: true
name: oidc
- Using external secret:
config:
oidc:
secret:
create: false
externalSecret:
enabled: true
name: your-oidc-secret
Deployment Configuration
| Key | Type | Default | Description |
|---|---|---|---|
| replicaCount | int | 1 |
Number of desired pods |
| image.registry | string | "ghcr.io" |
Container image registry |
| image.repository | string | "headlamp-k8s/headlamp" |
Container image name |
| image.tag | string | "" |
Container image tag (defaults to Chart appVersion) |
| image.pullPolicy | string | "IfNotPresent" |
Image pull policy |
| imagePullSecrets | list | [] |
Image pull secrets references |
| nameOverride | string | "" |
Override the name of the chart |
| fullnameOverride | string | "" |
Override the full name of the chart |
| namespaceOverride | string | "" |
Override the deployment namespace; defaults to .Release.Namespace |
| initContainers | list | [] |
Init containers to run before main container |
Security Configuration
| Key | Type | Default | Description |
|---|---|---|---|
| automountServiceAccountToken | bool | true |
Mount Service Account token in pod |
| serviceAccount.create | bool | true |
Create service account |
| serviceAccount.name | string | "" |
Service account name |
| serviceAccount.annotations | object | {} |
Service account annotations |
| clusterRoleBinding.create | bool | true |
Create cluster role binding |
| clusterRoleBinding.clusterRoleName | string | "cluster-admin" |
Kubernetes ClusterRole name |
| clusterRoleBinding.annotations | object | {} |
Cluster role binding annotations |
| hostUsers | bool | true |
Run in host uid namespace |
| podSecurityContext | object | {} |
Pod security context (e.g., fsGroup: 2000) |
| securityContext.runAsNonRoot | bool | true |
Run container as non-root |
| securityContext.privileged | bool | false |
Run container in privileged mode |
| securityContext.runAsUser | int | 100 |
User ID to run container |
| securityContext.runAsGroup | int | 101 |
Group ID to run container |
| securityContext.capabilities | object | {} |
Container capabilities (e.g., drop: [ALL]) |
| securityContext.readOnlyRootFilesystem | bool | false |
Mount root filesystem as read-only |
NOTE: for hostUsers=false user namespaces must be supported. See: https://kubernetes.io/docs/concepts/workloads/pods/user-namespaces/
Storage Configuration
| Key | Type | Default | Description |
|---|---|---|---|
| persistentVolumeClaim.enabled | bool | false |
Enable PVC |
| persistentVolumeClaim.annotations | object | {} |
PVC annotations |
| persistentVolumeClaim.size | string | "" |
PVC size (required if enabled) |
| persistentVolumeClaim.storageClassName | string | "" |
Storage class name |
| persistentVolumeClaim.accessModes | list | [] |
PVC access modes |
| persistentVolumeClaim.selector | object | {} |
PVC selector |
| persistentVolumeClaim.volumeMode | string | "" |
PVC volume mode |
| volumeMounts | list | [] |
Container volume mounts |
| volumes | list | [] |
Pod volumes |
Network Configuration
| Key | Type | Default | Description |
|---|---|---|---|
| service.type | string | "ClusterIP" |
Kubernetes service type |
| service.port | int | 80 |
Kubernetes service port |
| ingress.enabled | bool | false |
Enable ingress |
| ingress.ingressClassName | string | "" |
Ingress class name |
| ingress.annotations | object | {} |
Ingress annotations (e.g., kubernetes.io/tls-acme: "true") |
| ingress.labels | object | {} |
Additional labels for the Ingress resource |
| ingress.hosts | list | [] |
Ingress hosts configuration |
| ingress.tls | list | [] |
Ingress TLS configuration |
Example ingress configuration:
ingress:
enabled: true
annotations:
kubernetes.io/tls-acme: "true"
labels:
app.kubernetes.io/part-of: traefik
environment: prod
hosts:
- host: headlamp.example.com
paths:
- path: /
type: ImplementationSpecific
tls:
- secretName: headlamp-tls
hosts:
- headlamp.example.com
HTTPRoute Configuration (Gateway API)
For users who prefer Gateway API over classic Ingress resources, Headlamp supports HTTPRoute configuration.
| Key | Type | Default | Description |
|---|---|---|---|
| httpRoute.enabled | bool | false |
Enable HTTPRoute resource for Gateway API |
| httpRoute.annotations | object | {} |
Annotations for HTTPRoute resource |
| httpRoute.labels | object | {} |
Additional labels for HTTPRoute resource |
| httpRoute.parentRefs | list | [] |
Parent gateway references (REQUIRED when enabled) |
| httpRoute.hostnames | list | [] |
Hostnames for the HTTPRoute |
| httpRoute.rules | list | [] |
Custom routing rules (optional, defaults to path prefix /) |
Example HTTPRoute configuration:
httpRoute:
enabled: true
annotations:
gateway.example.com/custom-annotation: "value"
labels:
app.kubernetes.io/component: ingress
parentRefs:
- name: my-gateway
namespace: gateway-namespace
hostnames:
- headlamp.example.com
# Optional custom rules (defaults to path prefix / if not specified)
rules:
- matches:
- path:
type: PathPrefix
value: /headlamp
backendRefs:
- name: my-headlamp
port: 80
Resource Management
| Key | Type | Default | Description |
|---|---|---|---|
| resources | object | {} |
Container resource requests/limits |
| nodeSelector | object | {} |
Node labels for pod assignment |
| tolerations | list | [] |
Pod tolerations |
| affinity | object | {} |
Pod affinity settings |
| topologySpreadConstraints | list | [] |
Topology spread constraints for pod assignment |
| podAnnotations | object | {} |
Pod annotations |
| podLabels | object | {} |
Pod labels |
| env | list | [] |
Additional environment variables |
Example resource configuration:
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
Example environment variables:
env:
- name: KUBERNETES_SERVICE_HOST
value: "localhost"
- name: KUBERNETES_SERVICE_PORT
value: "6443"
Example topology spread constraints:
# Spread pods across availability zones with best-effort scheduling
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway # Prefer spreading but allow scheduling even if it violates the constraint
matchLabelKeys:
- pod-template-hash
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule # Hard requirement - don't schedule if it violates the constraint
matchLabelKeys:
- pod-template-hash
The labelSelector is automatically populated with the pod's selector labels if not specified. You can also provide a custom labelSelector:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: headlamp
custom-label: value
Pod Disruption Budget (PDB)
| Key | Type | Default | Description |
|---|---|---|---|
| podDisruptionBudget.enabled | bool | false |
Create a PodDisruptionBudget resource |
| podDisruptionBudget.minAvailable | integer | string | null | 0 |
Minimum pods that must be available. Rendered only when set to a positive integer or a percentage string (e.g. "1" or "50%"). Schema default is 0, but the chart skips rendering 0. |
| podDisruptionBudget.maxUnavailable | integer | string | null | null |
Maximum pods allowed to be unavailable. Accepts integer >= 0 or percentage string. Mutually exclusive with minAvailable; the template renders this field when set. |
| podDisruptionBudget.unhealthyPodEvictionPolicy | string | null | null |
Eviction policy: "IfHealthyBudget" or "AlwaysAllow". Emitted only on clusters running Kubernetes >= 1.27 and when explicitly set in values. |
Note: Ensure minAvailable and maxUnavailable are not both set (use null to disable one). To include minAvailable in the rendered PDB, set a positive integer or percentage; the template omits a 0 value.
Example, Require at least 1 pod available (ensure maxUnavailable is disabled):
podDisruptionBudget:
enabled: true
minAvailable: 1
maxUnavailable: null
Example, Allow up to 50% of pods to be unavailable:
podDisruptionBudget:
enabled: true
maxUnavailable: "50%"
minAvailable: null
Example, Set unhealthyPodEvictionPolicy (requires Kubernetes >= 1.27):
podDisruptionBudget:
enabled: true
maxUnavailable: 1
minAvailable: null
unhealthyPodEvictionPolicy: "IfHealthyBudget"
Ensure your replicaCount and maintenance procedures respect the configured PDB to avoid blocking intended operations.
pluginsManager Configuration
| Key | Type | Default | Description |
|---|---|---|---|
| enabled | boolean | false |
Enable plugin manager |
| configFile | string | plugin.yml |
Plugin configuration file name |
| configContent | string | "" |
Plugin configuration content in YAML format. This is required if plugins.enabled is true. |
| baseImage | string | node:lts-alpine |
Base node image to use |
| version | string | latest |
Headlamp plugin package version to install |
| env | list | [] |
Plugin manager env variable configuration |
| resources | object | {} |
Plugin manager resource requests/limits |
| volumeMounts | list | [] |
Plugin manager volume mounts |
Example resource configuration:
pluginsManager:
enabled: true
baseImage: node:lts-alpine
version: latest
env:
- name: HTTPS_PROXY
value: "proxy.example.com:8080"
resources:
requests:
cpu: "500m"
memory: "2048Mi"
limits:
cpu: "1"
memory: "4Gi"
Contributing
We welcome contributions to the Headlamp Helm chart! To contribute:
-
Fork the repository and create your branch from
main. -
Make your changes and test them thoroughly.
-
Run Helm chart template tests to ensure your changes don't break existing functionality:
$ make helm-template-testThis command executes the script at
charts/headlamp/tests/test.shto validate Helm chart templates against expected templates. -
If you've made changes that intentionally affect the rendered templates (like version updates or new features):
$ make helm-update-template-versionThis updates the expected templates with the current versions from Chart.yaml and only shows files where versions changed.
-
Review the updated templates carefully to ensure they contain only your intended changes.
-
Submit a pull request with a clear description of your changes.
For more details, refer to our contributing guidelines.