Snippets · Sources
Collect node, pod and container metrics from the kubelet
Each kubelet already knows the resource usage of its node, pods and containers. The kubelet_stats receiver, one per node as a DaemonSet, reads it from the kubelet's stats API on port 10250 with the pod's service account. The only RBAC it needs is get on nodes/stats: in our kind cluster that alone produced 219 metrics per scrape, with no errors.
- Signals
- Metrics
- Category
- CollectionKubernetes
- Source
- Kubernetes kubelet (stats API)
- Components
- batchmemory_limiterotlphttp
- Destination
- OTLP/HTTP endpoint
OpenTelemetry Collector
receivers:
kubelet_stats:
collection_interval: 30s
auth_type: serviceAccount
endpoint: https://${env:K8S_NODE_NAME}:10250
# Kubelet serving certificates are often self-signed; set this to false
# where the kubelet has a CA-signed certificate.
insecure_skip_verify: true
metric_groups: [node, pod, container]
processors:
memory_limiter:
check_interval: 1s
limit_percentage: 80
spike_limit_percentage: 25
batch:
timeout: 5s
exporters:
otlphttp:
endpoint: https://your-backend:4318
service:
pipelines:
metrics:
receivers: [kubelet_stats]
processors: [memory_limiter, batch]
exporters: [otlphttp]
The collector needs this to run in a cluster: a ServiceAccount, the RBAC it uses and the workload. Create the config first with kubectl -n observability create configmap kubeletstats --from-file=config.yaml.
apiVersion: v1
kind: ServiceAccount
metadata:
name: kubeletstats
namespace: observability
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kubeletstats
rules:
- apiGroups: [""]
resources: [nodes/stats]
verbs: [get]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kubeletstats
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kubeletstats
subjects:
- kind: ServiceAccount
name: kubeletstats
namespace: observability
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kubeletstats
namespace: observability
spec:
selector:
matchLabels:
app: kubeletstats
template:
metadata:
labels:
app: kubeletstats
spec:
serviceAccountName: kubeletstats
containers:
- name: otelcol
image: otel/opentelemetry-collector-contrib:0.161.0
args: ["--config=/conf/config.yaml"]
env:
- name: K8S_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
volumeMounts:
- name: config
mountPath: /conf
volumes:
- name: config
configMap:
name: kubeletstats
LinkMesh
Library › Sources › Kubernetes Node Metrics (kubeletstats)
- Collection Interval
- 20s
- Auth Type
- serviceAccount
- Kubelet Endpoint
- ${env:K8S_NODE_NAME}:10250
- Skip TLS Verify
- true
- Metric Groups
- ["node","pod","container"]
What LinkMesh then pushes to the collector, under receivers:
kubeletstats/input_src_00000501:
auth_type: serviceAccount
collection_interval: 20s
endpoint: ${env:K8S_NODE_NAME}:10250
insecure_skip_verify: true
metric_groups:
- node
- pod
- container
The same, as an API call (see automation recipes):
curl -X POST "$LINKMESH/sources" \
-H "Authorization: Bearer $LMSAT" \
-H "Content-Type: application/json" \
-d '{
"defaultConfig": {
"auth_type": "serviceAccount",
"collection_interval": "20s",
"endpoint": "${env:K8S_NODE_NAME}:10250",
"insecure_skip_verify": true,
"metric_groups": [
"node",
"pod",
"container"
]
},
"name": "Kubernetes Node Metrics (kubeletstats)",
"receiverType": "kubeletstats"
}'The rest of the collector config above (kubelet_stats, memory_limiter, batch, otlphttp) is not part of this template: in LinkMesh those are separate sources, processors and destinations on the route.
Generated from LinkMesh's template catalogue by linkmesh-server snippet-render 1.464.3, not written by hand.
Download config.yamlValidated with otelcol-contrib v0.161.0 on 2026-10-26
Complete config files, each checked with the collector's own validate command (otelcol-contrib v0.161.0). Replace https://your-backend:4318 with your own OTLP endpoint before you run it.
The gotcha
Kubelet serving certificates are usually self-signed. In our test, without insecure_skip_verify, every scrape failed with "tls: failed to verify certificate: x509: certificate signed by unknown authority" and no metric arrived. Keep insecure_skip_verify: true unless your kubelets have CA-signed serving certificates.
Manage this across a fleet
LinkMesh manages collector configuration across a fleet from one self-hosted control plane, so a change like this is made once rather than host by host. The first 25 Collectors are free after a no-card registration in the OpenSight Customer Portal (5 without one).
Doing this in LinkMesh: Install LinkMesh on Kubernetes
Related snippets
- Scrape cAdvisor container metrics from the kubeletThe container_* metrics Kubernetes dashboards expect, scraped from each node's kubelet with the pod's service account token.
- Collect Kubernetes cluster metrics (one replica)Deployment replicas, pod phases, node conditions, restarts and resource quotas from the Kubernetes API - one collector for the whole cluster.
- Collect Kubernetes events as logsScheduling failures, image pull errors, OOM kills and probe failures as log records - kept after Kubernetes deletes the events.
- Kubernetes pod logs to Loki, enriched with deployment and clusterEvery pod's logs into Loki over OTLP, labelled with namespace, pod, container, deployment, node and cluster.