Have you ever wondered what actually happens after you create a Pod? Or how the Kubernetes API server knows whether a node is healthy?
Although the API server is the central component of Kubernetes, it does not start containers, monitor nodes, or keep workloads running.
Those responsibilities belong to the kubelet.
The kubelet is one of the most important components in Kubernetes. Every workload that runs on a node ultimately depends on it. Without the kubelet, Kubernetes would simply be a collection of API objects stored in etcd—nothing would actually execute.
In this article, we'll explore how the kubelet works, how it communicates with the control plane, how it starts containers, and why it is often considered the backbone of every Kubernetes node.
What is the Kubelet?
Every Kubernetes node requires two major components before it can run workloads:
- A Container Runtime, such as containerd or CRI-O
- The kubelet
The kubelet is a process that runs on every node, typically as a systemd service.
Its responsibilities include:
- Communicating with the Kubernetes API server
- Registering the node
- Monitoring node health
- Watching for assigned Pods
- Creating and restarting containers
- Executing health probes
- Reporting status back to the control plane
In short, the kubelet continuously works to ensure the actual state of the node matches Kubernetes' desired state.
Monitoring Node Health
One of the kubelet's first responsibilities is monitoring the node on which it runs.
Every few seconds, it gathers information such as:
- CPU availability
- Memory availability
- Disk usage
- Filesystem capacity
- Process limits
- Network availability
- Container runtime health
- Kernel status
- Swap configuration
The kubelet periodically sends this information back to the API server by updating the Node object.
This is how Kubernetes determines whether a node is healthy enough to schedule workloads.
Installing and Starting the Kubelet
On most Linux distributions, the kubelet is installed as a package.
Ubuntu:
apt install kubelet
RHEL/CentOS:
yum install kubelet
Because the kubelet runs as a systemd service, you can inspect its status with:
systemctl status kubelet
A typical kubelet service consists of:
kubelet.service
├── kubelet binary
├── configuration
├── certificates
└── kubeconfig
Immediately after startup, the kubelet begins:
- Reading its configuration
- Discovering node resources
- Loading certificates
- Connecting to the API server
- Registering the node
A machine does not become part of a Kubernetes cluster until the kubelet successfully starts.
Kubelet Configuration
Every kubelet reads configuration that defines how it behaves.
The configuration file is commonly located at:
/var/lib/kubelet/config.yaml
Example:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
Some of the most important settings include:
clusterDNSclusterDomainstaticPodPathcgroupDriver
clusterDNS
This specifies the DNS server that Pods use for service discovery.
For example:
kubectl exec -it mygood -- cat /etc/resolv.conf
Example output:
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
Notice that the nameserver matches the ClusterDNS IP.
You can verify the ClusterDNS Service:
kubectl -n kube-system get svc kube-dns
Every Pod receives this DNS server automatically.
clusterDomain
This specifies the DNS suffix used throughout the cluster.
Suppose you create:
kubectl create ns kubelet
kubectl -n kubelet run mynginx --image=nginx
kubectl -n kubelet expose pod mynginx \
--port 80 \
--name kube-svc
kubectl run mybusy \
--image=busybox \
-- sh -c "sleep infinity"
Now test DNS resolution:
kubectl exec -it mybusy -- \
wget --timeout=5 --spider \
kube-svc.kubelet.svc.cluster.local
Example output:
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: kube-svc.kubelet.svc.cluster.local
Address: 10.104.169.148
The DNS server resolves the fully qualified service name to the ClusterIP of the Service.
staticPodPath
The kubelet continuously watches a directory containing Static Pod manifests.
Typically:
/etc/kubernetes/manifests
Any Pod manifest placed inside this directory is started automatically.
This setting is particularly important on control-plane nodes because it is how Kubernetes bootstraps itself.
cgroupDriver
The kubelet and the container runtime must use compatible cgroup drivers.
Supported options include:
systemdcgroupfs
To verify containerd's configuration:
containerd config dump | grep SystemdCgroup
To modify it:
vim /etc/containerd/config.toml
After making changes:
systemctl restart containerd
A mismatch between the kubelet and the container runtime is a common source of cluster instability.
How the Kubelet Registers a Node
Most worker nodes join a cluster using a command generated by:
kubeadm token create --print-join-command
Example:
sudo kubeadm join 10.0.0.10:6443 \
--token abcdef.1234567890abcdef \
--discovery-token-ca-cert-hash sha256:...
Behind the scenes, several things happen.
- kubeadm authenticates using the bootstrap token.
- kubeadm generates the kubelet configuration.
- kubelet starts.
- kubelet creates a Certificate Signing Request (CSR).
- The API server approves the CSR.
- kubelet receives a client certificate.
- kubelet registers the node.
The process looks like this:
kubeadm join
│
▼
Bootstrap Authentication
│
▼
Generate kubelet Configuration
│
▼
Start kubelet
│
▼
Create CSR
│
▼
API Server
│
▼
Certificate Approved
│
▼
kubelet.conf Updated
│
▼
Register Node
After registration, the kubelet authenticates as the node itself.
For example:
kubectl \
--kubeconfig /etc/kubernetes/kubelet.conf \
auth whoami
Example:
Username: system:node:worker1
Groups:
system:nodes
system:authenticated
You can verify registered nodes from the control plane:
kubectl get nodes
Example:
NAME STATUS ROLES
master Ready control-plane
worker1 Ready <none>
How the Kubelet Watches for Pods
Once registered, the kubelet begins watching the API server for Pods assigned to its node.
Suppose the scheduler assigns a Pod:
spec:
nodeName: worker1
The kubelet running on worker1 notices that assignment.
It retrieves the Pod specification.
Example:
containers:
- name: nginx
image: nginx
The kubelet then enters its reconciliation loop.
Pod exists?
│
No
▼
Create Pod
Container running?
│
No
▼
Start Container
Container healthy?
│
No
▼
Restart Container
This loop never stops.
The kubelet continuously compares the desired state with reality and takes corrective action whenever necessary.
Static Pods
Static Pods are managed directly by the kubelet instead of the API server.
Rather than retrieving Pod definitions from Kubernetes, the kubelet watches the directory specified by staticPodPath.
Usually:
/etc/kubernetes/manifests
Suppose you place the following file there:
apiVersion: v1
kind: Pod
metadata:
name: nginx
The kubelet immediately detects the new manifest and starts the Pod.
Manifest Created
│
▼
Kubelet Detects File
│
▼
Container Created
No scheduler is involved.
No Deployment is required.
The API server does not initiate the process.
Static Pods are the mechanism Kubernetes uses to bootstrap its own control plane.
Notice that control-plane Static Pods include the node name in their Pod names.
Example:
etcd-cluster3
kube-apiserver-cluster3
kube-controller-manager-cluster3
kube-scheduler-cluster3
How the Kubernetes Control Plane Starts
One of the most fascinating aspects of Kubernetes is that the control plane itself depends on the kubelet.
When you execute:
kubeadm init
kubeadm does not directly start:
- kube-apiserver
- kube-controller-manager
- kube-scheduler
- etcd
Instead, it writes manifest files into:
/etc/kubernetes/manifests
Example:
kube-apiserver.yaml
kube-controller-manager.yaml
kube-scheduler.yaml
etcd.yaml
The kubelet watches this directory.
As soon as it detects those files, it starts the containers.
The startup sequence looks like this:
Kubelet Starts
│
▼
Reads Static Pod Files
│
▼
Starts API Server
│
▼
API Server Becomes Available
│
▼
Cluster Begins Operating
Without the kubelet, the Kubernetes control plane would never start.
Common Kubelet Failure Scenarios
Understanding kubelet failures makes troubleshooting Kubernetes significantly easier.
Kubelet Stops
systemctl stop kubelet
Effects:
- Existing containers continue running.
- New Pods cannot start.
- The node eventually becomes NotReady.
API Server Failure
If the API server becomes unavailable:
- Existing Pods continue running.
- No new workloads are scheduled.
- Cluster state cannot be updated.
Container Runtime Failure
If containerd crashes:
Kubelet
│
▼
Container Runtime Unavailable
Effects:
- New containers cannot start.
- Container restarts fail.
- Pod lifecycle management stops.
Certificate Expiration
If kubelet certificates expire:
- Authentication fails.
- Heartbeats stop.
- The node becomes unreachable.
Swap Enabled
Many Kubernetes installations require swap to be disabled.
Check:
swapon --show
Typical error:
running with swap on is not supported
Disable swap:
swapoff -a
For permanent changes, remove swap entries from /etc/fstab.
Cgroup Driver Mismatch
Example:
Kubelet:
systemd
containerd:
cgroupfs
Possible effects:
- Kubelet startup failures
- Pod creation failures
- Nodes repeatedly transitioning between Ready and NotReady
Missing Sysctl Settings
Kubernetes networking depends on kernel parameters such as:
net.bridge.bridge-nf-call-iptables
net.ipv4.ip_forward
Configure them:
cat <<EOF >/etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sysctl -p /etc/sysctl.d/k8s.conf
Troubleshooting the Kubelet
The first place to investigate kubelet issues is the systemd journal.
View logs:
journalctl -u kubelet
Follow logs in real time:
journalctl -u kubelet -f
Common errors include:
running with swap on is not supported
failed to run Kubelet
failed to validate cgroup driver
certificate has expired
container runtime is down
When a node unexpectedly becomes NotReady, the kubelet journal is often the quickest way to identify the underlying problem.
Conclusion
The kubelet is frequently described as a node agent, but that description hardly captures its importance.
The kubelet is responsible for:
- Registering nodes
- Reporting node health
- Creating Pods
- Restarting containers
- Executing probes
- Mounting volumes
- Communicating with the container runtime
- Bootstrapping the Kubernetes control plane itself
Without the kubelet, Kubernetes would simply be a collection of objects stored in etcd.
Nothing would actually run.
The next time you deploy an application and watch a Pod transition from Pending to Running, remember what really happened:
The scheduler decided where the Pod should run.
The API server stored that decision.
But it was the kubelet that turned that desired state into a running container.