kube-prometheus-stack: the full monitoring setup

Published: 2026-03-18

kube-prometheus-stack is a single Helm chart that deploys Prometheus, Alertmanager, Grafana, node-exporter, kube-state-metrics, and all the default recording rules and dashboards. It's the standard way to get Kubernetes observability up in one shot.


HelmRelease

yamlapiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: kube-prom-stack
  namespace: monitoring
spec:
  chart:
    spec:
      chart: kube-prometheus-stack
      version: "65.x"
      sourceRef:
        kind: HelmRepository
        name: prometheus-community
        namespace: flux-system
  values:
    fullnameOverride: kube-prom-stack

    prometheus:
      prometheusSpec:
        retention: 30d
        retentionSize: 40GB
        storageSpec:
          volumeClaimTemplate:
            spec:
              storageClassName: local-path
              resources:
                requests:
                  storage: 50Gi
        serviceMonitorSelectorNilUsesHelmValues: false
        podMonitorSelectorNilUsesHelmValues: false
        ruleSelectorNilUsesHelmValues: false
        resources:
          requests:
            cpu: 500m
            memory: 2Gi
          limits:
            cpu: 2000m
            memory: 4Gi

    alertmanager:
      alertmanagerSpec:
        storage:
          volumeClaimTemplate:
            spec:
              storageClassName: local-path
              resources:
                requests:
                  storage: 5Gi

    grafana:
      adminPassword: ""    # set via secret
      persistence:
        enabled: true
        storageClassName: local-path
        size: 10Gi
      grafana.ini:
        server:
          root_url: https://grafana.example.com
      sidecar:
        dashboards:
          enabled: true
          searchNamespace: ALL

    nodeExporter:
      enabled: true

    kubeStateMetrics:
      enabled: true

serviceMonitorSelectorNilUsesHelmValues: false is critical. Without it, Prometheus only watches ServiceMonitors in the release namespace. Setting it to false makes Prometheus watch all namespaces. Same for podMonitorSelectorNilUsesHelmValues and ruleSelectorNilUsesHelmValues.


Grafana admin password via secret

yamlgrafana:
  admin:
    existingSecret: grafana-admin-secret
    userKey: admin-user
    passwordKey: admin-password
bashkubectl create secret generic grafana-admin-secret \
  --from-literal=admin-user=admin \
  --from-literal=admin-password=REDACTED \
  --dry-run=client -o yaml \
  | kubeseal --cert infra.pem -o yaml \
  > fluxcd/custom/monitoring/grafana-admin-secret.yaml

ServiceMonitor for your app

yamlapiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-service
  namespace: app
  labels:
    release: kube-prom-stack    # must match Prometheus's serviceMonitorSelector
spec:
  selector:
    matchLabels:
      app: my-service
  namespaceSelector:
    matchNames:
      - app
  endpoints:
    - port: http
      path: /metrics
      interval: 30s

The release: kube-prom-stack label is required. Without it, Prometheus ignores the ServiceMonitor.


Retention and storage tuning

With retention: 30d and retentionSize: 40GB, Prometheus drops data when either limit is reached.

For a medium cluster (50 nodes, 200 pods):

  • ~10 MB/s ingest rate
  • Prometheus uses snappy compression (~70% reduction)
  • Effective: ~7.5 GB/day
  • 30 days ≈ 225 GB compressed

50 GB is tight. Tune retention down to 14d, increase the PVC, or use remote_write to VictoriaMetrics for long-term storage.


Verifying everything works

bash# Prometheus targets
kubectl port-forward -n monitoring svc/kube-prom-stack-prometheus 9090:9090
# http://localhost:9090/targets — all should be UP

# Check Alertmanager config
kubectl port-forward -n monitoring svc/kube-prom-stack-alertmanager 9093:9093
# http://localhost:9093/#/status

# Check a ServiceMonitor is picked up
kubectl get servicemonitor -A | grep my-service
# Then check http://localhost:9090/targets for your service

Default dashboards included

Out of the box:

  • Kubernetes / Nodes — CPU, memory, disk per node
  • Kubernetes / Pods — per-pod resource usage
  • Kubernetes / Namespaces — aggregate per namespace
  • Alertmanager — alert history and routing
  • Node Exporter — full hardware metrics

Adding custom dashboards

yamlapiVersion: v1
kind: ConfigMap
metadata:
  name: my-custom-dashboard
  namespace: monitoring
  labels:
    grafana_dashboard: "1"
data:
  my-dashboard.json: |
    { ... }

With sidecar.dashboards.searchNamespace: ALL, Grafana picks up dashboards from any namespace. Deploy the ConfigMap with your application.


Alertmanager configuration

yamlalertmanager:
  config:
    global:
      resolve_timeout: 5m
    route:
      group_by: ['alertname', 'job']
      group_wait: 30s
      group_interval: 5m
      repeat_interval: 12h
      receiver: 'telegram'
      routes:
        - matchers:
            - severity = "critical"
          receiver: 'telegram-critical'
    receivers:
      - name: 'telegram'
        telegram_configs:
          - bot_token: "${TELEGRAM_BOT_TOKEN}"
            chat_id: -123456789
            message: "{{ range .Alerts }}{{ .Annotations.description }}\n{{ end }}"
      - name: 'telegram-critical'
        telegram_configs:
          - bot_token: "${TELEGRAM_BOT_TOKEN}"
            chat_id: -987654321