kube-prometheus-stack¶
kube-prometheus-stack is a Helm chart that deploys Prometheus, Grafana, and a constellation of exporters and operators. Most of it converts cleanly via the servicemonitor extension. Grafana does not.
The problem: Grafana in kube-prometheus-stack ships with k8s-sidecar containers that watch ConfigMaps/Secrets via the Kubernetes API at runtime. They provision dashboards and datasources by polling the apiserver for labeled ConfigMaps. This has no compose equivalent — there is no apiserver to poll.
The acolyte built a mirror-temple, faithful in every stone — yet the oracles within fell silent, for the gods they consulted resided in a firmament this world had never known. The prayers were correct; the heavens were absent.
— Cultes des Goules, On Oracles Without Firmament (so they say)
What to exclude¶
The sidecar containers and the admission webhooks serve no purpose in compose:
# dekube.yaml
exclude:
- kube-prometheus-stack-grafana-sidecar-grafana*
- kube-prometheus-stack-admission*
Grafana override¶
The original Grafana service uses kiwigrid/k8s-sidecar as the main image (the sidecar container is first in the pod spec). Override it with the actual Grafana image and provide env vars directly:
# dekube.yaml
overrides:
kube-prometheus-stack-grafana:
image: docker.io/grafana/grafana:12.3.1 # adapt to your version
container_name: null
environment:
GF_SECURITY_ADMIN_USER: $secret:grafana-admin-secret:username
GF_SECURITY_ADMIN_PASSWORD: $secret:grafana-admin-secret:password
GF_PATHS_DATA: /var/lib/grafana/
GF_PATHS_LOGS: /var/log/grafana
GF_PATHS_PLUGINS: /var/lib/grafana/plugins
GF_PATHS_PROVISIONING: /etc/grafana/provisioning
volumes:
- ./configmaps/kube-prometheus-stack-grafana/grafana.ini:/etc/grafana/grafana.ini:ro
- ./data/kube-prometheus-stack-grafana:/var/lib/grafana
- /var/lib/grafana-search
- ./configmaps/kube-prometheus-stack-grafana-dashboards-custom/my-dashboard.json:/var/lib/grafana/dashboards/custom/my-dashboard.json:ro
- ./configmaps/kube-prometheus-stack-grafana/dashboardproviders.yaml:/etc/grafana/provisioning/dashboards/dashboardproviders.yaml:ro
- ./configmaps/kube-prometheus-stack-grafana-config-dashboards/provider.yaml:/etc/grafana/provisioning/dashboards/sc-dashboardproviders.yaml:ro
- ./configmaps/kube-prometheus-stack-grafana-datasource/datasource.yaml:/etc/grafana/provisioning/datasources/datasource.yaml:ro
Adapt the secret name, dashboard paths, and Grafana image version to your setup — the version shown above is a snapshot that will go stale.
Datasource provisioning¶
In K8s, the k8s-sidecar populates /etc/grafana/provisioning/datasources/ from a labeled ConfigMap. In compose, create the file statically.
The datasource ConfigMap is typically rendered by the Helm chart and already present in your manifests — helmfile2compose writes it to configmaps/kube-prometheus-stack-grafana-datasource/. Mount it as shown in the override above.
If the datasource references the Prometheus K8s Service by its FQDN (e.g. kube-prometheus-stack-prometheus.monitoring.svc.cluster.local:9090), it resolves natively via network aliases — no replacement needed.
Dashboard provisioning¶
Same pattern: the Helm chart renders dashboard JSON as ConfigMaps. helmfile2compose writes them to configmaps/. Mount each dashboard JSON into Grafana's dashboard directory and provide a dashboardproviders.yaml that points at it.
The chart's default dashboardproviders.yaml ConfigMap usually works as-is — mount it from configmaps/kube-prometheus-stack-grafana/dashboardproviders.yaml.
Other components to exclude¶
kube-prometheus-stack also includes several components that serve no purpose in compose:
kube-prometheus-stack-operator— the Prometheus Operator itself (no CRDs to reconcile in compose)kube-prometheus-stack-kube-state-metrics— needs the Kubernetes APIkube-prometheus-stack-prometheus-node-exporter— needs host-level access
These are not auto-excluded by helmfile2compose — add them manually to your exclude: list: