Introduction
Kubernetes node administrators juggle numerous flags when starting the kubelet. Flags clutter scripts and make consistent configuration across nodes harder to manage. Modern Kubernetes lets you move most of those options into a structured on-disk config file that follows the KubeletConfiguration API. You then launch the kubelet with a single --config flag pointing at that file. This approach streamlines node deployment and centralises configuration management.
Estimated reading time: 7 minutes
TL;DR
- Kubelet supports a YAML config file (
KubeletConfiguration) as an alternative to, and override-able by, command-line flags. - You can use
curlandjqto fetch and inspect kubelet configuration on managed nodes (for example, via/configzor distro-specific endpoints). - Define a
KubeletConfigurationmanifest and pass it to the kubelet via--config=/path/to/kubelet-config.yaml(or via kubeadm’skubeletConfiguration). - The historical Dynamic Kubelet Configuration feature (ConfigMap-driven, often described with
--dynamic-config-dir) was deprecated in v1.22 and removed in v1.24. On current clusters you roll out kubelet config changes by updating the file and restarting or rolling the node. - For persistent volumes, use CSI drivers + StorageClasses for dynamic provisioning; kubelet integrates with those drivers via Unix sockets rather than explicit per-driver settings in the kubelet config file.
- Combine Pod
securityContext, Pod Security Admission / Pod Security Standards, RBAC, and ResourceQuotas/LimitRanges to limit what workloads can do on a node; kubelet then enforces those runtime constraints.
Kubelet Config File: Overview
The kubelet reads its configuration from a file when you supply the --config flag. The file must conform to the kubelet.config.k8s.io/v1beta1 KubeletConfiguration API. This lets you replace dozens of command-line flags with a single manifest that can be version-controlled and pushed out across your fleet. Command-line flags still override values from the config file, preserving backwards compatibility.
This mechanism is independent of how you launch the kubelet. Regardless of whether it runs as a systemd service, a static binary, or in a container, you can point it at the same config file via --config.
Kubelet Config File: Prerequisites
Before you begin, install jq and curl. These tools make it easier to fetch and inspect kubelet configuration (for example, from /configz or from a distro-specific endpoint exposed by your node images).
# On Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y curl jq
# On CentOS/RHEL
sudo yum install -y curl jq
Kubelet Config File: Config Structure
The configuration file uses the KubeletConfiguration API. Save your file as kubelet-config.yaml (path is up to you; many distros use /var/lib/kubelet/config.yaml or /etc/kubernetes/kubelet.conf).
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
# Where the kubelet serves its HTTPS API
address: 0.0.0.0
port: 10250
# Disable the insecure read-only port
readOnlyPort: 0
# Commonly 'systemd' or 'cgroupfs', must match your container runtime configuration
cgroupDriver: systemd
# Cluster DNS and domain
clusterDomain: cluster.local
clusterDNS:
- 10.96.0.10
# Timeout for most runtime calls
runtimeRequestTimeout: 2m
This snippet shows a minimal, commonly customised subset. The full schema includes many more fields for authentication, authorization, resource reservations, eviction thresholds, logging, and more.
💡 Note on dynamicConfigDir
Older kubelet versions exposed a dynamicConfigDir field in KubeletConfiguration and supported the Dynamic Kubelet Configuration feature. That feature was deprecated in v1.22 and removed in v1.24, so it is not available on current supported releases.
Kubelet Config File: Applying Config
Next, point the kubelet to your config file. On systemd-managed nodes you typically add a drop-in that sets KUBELET_CONFIG_ARGS, which the main unit picks up in its ExecStart line. The exact unit name and path may differ by distribution; this example matches common kubeadm-based setups.
# systemd drop-in for kubelet
cat <<EOF | sudo tee /etc/systemd/system/kubelet.service.d/20-configfile.conf
[Service]
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config/kubelet-config.yaml"
EOF
sudo systemctl daemon-reload
sudo systemctl restart kubelet
If your distro uses a different unit name (for example, kubelet.service vs. k8s-kubelet.service) or a wrapper script, adapt the path and environment variable accordingly.
Dynamic Configuration: Current State (v1.24+)
Historically, Kubernetes had a feature called Dynamic Kubelet Configuration. You stored a KubeletConfiguration in a ConfigMap, pointed Node.spec.configSource at it, and kubelet would checkpoint that config to disk and restart itself to adopt the new settings.
However:
- The feature was deprecated in Kubernetes v1.22 and
- Removed in Kubernetes v1.24.
On current clusters you should not rely on --dynamic-config-dir or Node-level dynamic config. Instead:
- Manage
KubeletConfigurationfiles using your node lifecycle tooling (kubeadm, Cluster API, cloud-init, Ansible, etc.). - Roll changes out with controlled node restarts or blue-green node replacement.
This gives you a GitOps-friendly, reproducible path without depending on a removed feature.
Volume Management in Kubelet Configuration
Kubelet orchestrates both static and dynamically provisioned volumes:
Dynamic provisioning: you define StorageClass objects, and a CSI provisioner controller creates volumes on demand for PersistentVolumeClaims.
Static provisioning: you create PersistentVolume objects that reference existing storage (hostPath, NFS, iSCSI, etc.).
Most CSI drivers do not require any driver-specific entries in KubeletConfiguration. They are deployed as DaemonSets with sidecars like node-driver-registrar, and they register themselves with kubelet using Unix domain sockets under the kubelet’s root directory (for example, /var/lib/kubelet/plugins and /var/lib/kubelet/plugins_registry).
The one volume-related field you might set in the kubelet config is the directory for legacy FlexVolume plugins:
# For legacy FlexVolume plugins (in-tree / deprecated)
volumePluginDir: "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/"
By default, kubelet looks for third-party volume plugins in that path on many Linux distributions.
Common volume types include emptyDir, hostPath, configMap, secret, persistentVolumeClaim, and CSI volumes (via the csi volume type); the official docs list many more options such as projected, downwardAPI, and ephemeral.
Kubelet manages the per-pod mount lifecycle—attach, mount, unmount, detach—and tracks mounts and pod data under its root directory (typically /var/lib/kubelet, with per-Pod data under /var/lib/kubelet/pods).
Kubelet Config File: CSI and Dynamic Provisioning
CSI drivers are deployed as:
- a node plugin component (DaemonSet) that runs on every node and handles the node-local mount operations.
- a controller component (Deployment/StatefulSet) for provisioning and snapshotting, and
On each node:
- The CSI node plugin exposes a Unix socket (for example,
/var/lib/kubelet/plugins/<drivername>/csi.sock). - A
node-driver-registrarsidecar registers the driver with kubelet using a socket in/var/lib/kubelet/plugins_registry/. - Once registered, kubelet calls the CSI Node service (via the node plugin) to perform
NodeStageVolume/NodePublishVolumeoperations and make the volume available inside the pod.
Dynamic provisioning flow:
- A developer creates a
PersistentVolumeClaimthat references aStorageClasswhoseprovisionermatches your CSI driver. - The CSI external-provisioner controller watches PVCs and StorageClasses and issues
CreateVolume/DeleteVolumecalls to the driver. - When the PVC is bound to a PV and the Pod is scheduled, kubelet on the target node uses the registered CSI Node plugin to stage, mount, and later unmount the volume.
None of this requires listing CSI drivers inside kubelet-config.yaml; kubelet discovers them dynamically via the plugin registry.
Kubelet Config File: Capacity, Quotas, and Snapshots
Kubelet uses its configuration to calculate Node Allocatable and to control eviction behaviour. You can configure eviction thresholds and resource reservations directly in KubeletConfiguration.
evictionHard:
"memory.available": "200Mi"
"nodefs.available": "10%"
kubeReserved:
cpu: "100m"
memory: "256Mi"
ephemeral-storage: "1Gi"
kubeReservedCgroup: "/kube-reserved"
kubeReservedandsystemReservedcarve out CPU/memory/storage for system daemons and kubelet.kubeReservedCgrouptells kubelet which cgroup enforces those reservations, aligning node-level cgroup configuration with cluster-level expectations.
Snapshots
Volume snapshots are implemented at the storage layer via CSI:
- The external-snapshotter controller watches
VolumeSnapshotandVolumeSnapshotContentresources and calls CSI snapshot RPCs (CreateSnapshot,DeleteSnapshot). - Snapshot-backed volumes are exposed to pods as normal PVs/PVCs (often using a
dataSourcethat points to aVolumeSnapshot), so kubelet treats them like any other CSI-backed volume and mounts them the same way.
The Kubelet itself does not take or manage snapshots; it only mounts volumes created or restored by the CSI stack.
Kubelet Config File: Security Contexts and Access Controls
The Kubelet is responsible for enforcing the security settings that were admitted by the API server:
Once scheduled, kubelet uses the securityContext to configure the containers appropriately.
Each Pod and container can define a securityContext (user/group IDs, capabilities, SELinux labels, etc.).
The API server applies policies—such as Pod Security Admission enforcing the Pod Security Standards—to decide whether a Pod’s securityContext is allowed.
Important clarifications:
PodSecurityPolicyhas been deprecated and removed; modern clusters should not rely on PSP. Use Pod Security Admission + Pod Security Standards instead.- There is no
apparmorProfileRootfield in currentKubeletConfiguration. AppArmor profiles are selected via pod annotations (for example,container.apparmor.security.beta.kubernetes.io/<container>=localhost/<profile>), and the underlying Linux distribution controls where those profiles live on disk. - Seccomp defaults are configured with the
seccompDefaultoption inKubeletConfigurationor via the--seccomp-defaultflag, and the path for local profiles is controlled by the kubelet’s--seccomp-profile-rootflag (commonly pointing to a directory like/var/lib/kubelet/seccompor/etc/kubernetes/seccomp).
From a kubelet config perspective, security-relevant fields include:
authenticationandauthorizationblocks (client authn/authz for the kubelet API).readOnlyPort: 0to disable the insecure HTTP port.- TLS options like
tlsCertFile,tlsPrivateKeyFile, androtateCertificates. - Kernel-hardening options such as
protectKernelDefaultsandallowedUnsafeSysctls.
On the cluster control plane side, use:
- RBAC to restrict who can modify Node objects and any ConfigMaps / custom resources that your tooling uses to store kubelet config.
- ResourceQuotas and LimitRanges to prevent tenants from scheduling overly large or privileged workloads that can starve node resources.
For on-disk files, ensure:
- The kubelet config file (and any related TLS keys) is owned by
root:root. - Mode is typically
600or640, depending on your distro’s expectations.
This prevents non-privileged users on the node from tampering with kubelet configuration or credentials.
Use-Cases and Event-Driven Flows
You can integrate kubelet config changes into GitOps or general CI/CD flows:
- Store your
kubelet-config.yamlin Git. - Use an operator, configuration management tool, or node image pipeline to push updated configs onto nodes.
- Trigger controlled restarts or rolling node replacement (for example, via
systemctl restart kubelet, a machine-api rollout, or a node reboot orchestrator likekured).
On clusters running Kubernetes v1.24 and later, this is the recommended pattern. The removed Dynamic Kubelet Configuration feature used to provide API-driven, per-Node config updates, but today the stable path is “config file + node lifecycle management.”.
This event-driven approach still reduces manual SSH work: changes flow from Git into node configuration automatically, and the kubelet picks them up after a controlled restart.
References
- Kubernetes official: Set Kubelet Parameters Via A Configuration File
- jq tool documentation
- KubeletConfiguration API reference (v1beta1)
- Kubernetes Blog: Dynamic Kubelet Configuration
- Kubernetes CSI Developer Docs: Deploying a CSI Driver on Kubernetes – kubernetes
- Kubernetes Concepts: Volume Snapshots
- Kubernetes Tasks: Configure a Security Context for a Pod or Container
- Kubernetes Concepts: Using RBAC Authorization
- Kubernetes Concepts: Resource Quotas
Related Posts
Metadata
URLHash: 0c1a59ff349a1827478c036e7f05df52033836ca44b91663c1574a317ff26d7f
