Kubelet Config File Guide

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 curl and jq to fetch and inspect kubelet configuration on managed nodes (for example, via /configz or distro-specific endpoints).
  • Define a KubeletConfiguration manifest and pass it to the kubelet via --config=/path/to/kubelet-config.yaml (or via kubeadm’s kubeletConfiguration).
  • 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).


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).

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.

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 KubeletConfiguration files 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:

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-registrar sidecar 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 / NodePublishVolume operations and make the volume available inside the pod.

Dynamic provisioning flow:

  1. A developer creates a PersistentVolumeClaim that references a StorageClass whose provisioner matches your CSI driver.
  2. The CSI external-provisioner controller watches PVCs and StorageClasses and issues CreateVolume / DeleteVolume calls to the driver.
  3. 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.

  • kubeReserved and systemReserved carve out CPU/memory/storage for system daemons and kubelet.
  • kubeReservedCgroup tells 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 VolumeSnapshot and VolumeSnapshotContent resources and calls CSI snapshot RPCs (CreateSnapshot, DeleteSnapshot).
  • Snapshot-backed volumes are exposed to pods as normal PVs/PVCs (often using a dataSource that points to a VolumeSnapshot), 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:

  • PodSecurityPolicy has been deprecated and removed; modern clusters should not rely on PSP. Use Pod Security Admission + Pod Security Standards instead.
  • There is no apparmorProfileRoot field in current KubeletConfiguration. 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 seccompDefault option in KubeletConfiguration or via the --seccomp-default flag, and the path for local profiles is controlled by the kubelet’s --seccomp-profile-root flag (commonly pointing to a directory like /var/lib/kubelet/seccomp or /etc/kubernetes/seccomp).

From a kubelet config perspective, security-relevant fields include:

  • authentication and authorization blocks (client authn/authz for the kubelet API).
  • readOnlyPort: 0 to disable the insecure HTTP port.
  • TLS options like tlsCertFile, tlsPrivateKeyFile, and rotateCertificates.
  • Kernel-hardening options such as protectKernelDefaults and allowedUnsafeSysctls.

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 600 or 640, 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:

  1. Store your kubelet-config.yaml in Git.
  2. Use an operator, configuration management tool, or node image pipeline to push updated configs onto nodes.
  3. Trigger controlled restarts or rolling node replacement (for example, via systemctl restart kubelet, a machine-api rollout, or a node reboot orchestrator like kured).

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

Metadata

URLHash: 0c1a59ff349a1827478c036e7f05df52033836ca44b91663c1574a317ff26d7f

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.