Prerequisites: kubernetes

A Kubernetes manifest is a serialized Kubernetes object. YAML is only the notation used to write it. The useful mental model is not “YAML makes infrastructure” but “this document tells the Kubernetes API which object should exist and what state that object should hold.” The API validates it, stores it, and controllers act on it.

The Devata Overview dashboard begins with this shape:

apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-dashboard-devata-overview
  namespace: monitoring
  labels:
    grafana_dashboard: "1"
  annotations:
    grafana_folder: /tmp/dashboards/Devata
data:
  devata-overview.json: |-
    {
      "title": "Devata Overview"
    }

Reading the document from the outside in

FieldQuestion it answersDevata answer
apiVersionWhich API schema understands this object?Core Kubernetes API, version v1
kindWhat type of object is this?ConfigMap
metadata.nameWhat is its stable identity?grafana-dashboard-devata-overview
metadata.namespaceWhich namespace owns it?monitoring
metadata.labelsWhich machines or controllers should select it?The Grafana dashboard sidecar
metadata.annotationsWhat extra instruction travels with it?The destination folder path
dataWhat non-secret configuration does it carry?A key whose value is dashboard JSON

Not every Kubernetes object has a spec. Workload objects such as Deployments do because they describe a controller-managed desired state. A ConfigMap stores keys under data or binaryData, so the dashboard manifest correctly has no spec.

YAML details that matter here

Indentation expresses nesting. grafana_dashboard belongs to labels because it is indented beneath it. Moving it one level left would change the object, not merely its appearance.

"1" is quoted because Kubernetes label values are strings. Without the quotes a YAML parser may interpret 1 as a number, which does not match the API schema for labels.

|- starts a literal block scalar. It means “treat the following indented lines as one string, preserve their line breaks, and remove the final newline.” The JSON is therefore not a second Kubernetes object. To the ConfigMap it is one long string stored under the key devata-overview.json. Later, the sidecar turns that key into a file with the same name.

Labels and annotations are both metadata, but they carry different contracts here:

  • the label is a selector: grafana_dashboard=1 makes the object discoverable;
  • the annotation is an instruction: grafana_folder tells the sidecar where to write it.

That distinction is why changing either field can leave the ConfigMap healthy in Kubernetes while making the dashboard disappear from Grafana.

Validate before reconciliation

From the lab repository:

kubectl apply --dry-run=server \
  -f kubernetes/infra/observability/kps/resources/devata-overview-dashboard.yaml
 
kubectl diff \
  -f kubernetes/infra/observability/kps/resources/devata-overview-dashboard.yaml

The server-side dry run checks the document against the live API without storing it. kubectl diff compares the file with the live object. In this homelab the normal deployment path is still Git and application, not a manual kubectl apply; these commands are preflight tools.

Official reference: Kubernetes objects.