Kubernetes Volume Mounts: Hot Reload, subPath Pitfalls & Projected Volumes
This article explains Kubernetes volume mounting for ConfigMaps and Secrets, detailing how directory mounts achieve hot reloads via atomic symlink switching, why subPath mounts never update, and how projected volumes merge multiple configuration sources into a single directory.
1. Basic Directory Mount for ConfigMap
Environment variables suit a few short values, but applications reading entire config files (e.g., nginx.conf, application.yaml) should use volumes. Declare a volume and mount it to a container path:
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: config-vol
mountPath: /etc/config # mount to this directory in container
volumes:
- name: config-vol
configMap:
name: app-config # source: ConfigMap created earlierAfter mounting, each key in app-config becomes a file under /etc/config/; key app.properties maps to /etc/config/app.properties with the key's value as content. Secrets work identically — replace configMap with secret.
To mount only selected keys and optionally rename files, use items:
volumes:
- name: config-vol
configMap:
name: app-config
items:
- key: app.properties
path: application.yaml # key app.properties → filename becomes application.yaml2. Hot Reload Truth: Atomic Symlink Switch
When you update a ConfigMap and run kubectl apply, files in the mounted directory update automatically — no Pod restart needed. This is not magic; kubelet uses a symlink mechanism.
The mounted directory hides this structure:
The file the application opens (e.g., app.properties) is a symlink, not a real file.
It points to a symlink named ..data. ..data points to the actual content directory named with a timestamp.
When the ConfigMap changes, kubelet writes the new content into a new timestamped directory, then atomically switches the ..data symlink to point to the new directory. Symlink switching is atomic, so the application reads either the old complete version or the new complete version — never a half-written corrupt file .
⏱️ Hot reload is not "instant" From apply to actual file change in the container there is latency. Kubelet syncs periodically (default ~1 minute) plus a cache TTL, so worst case may take 1–2 minutes. Don't panic if nothing changes after two seconds.
3. Fatal Trap: subPath Never Hot Reloads
Now that you understand the symlink mechanism, the notorious pitfall is explained in one sentence. subPath is a common requirement: mount a single config file to a precise location without overwriting the entire directory (e.g., inject into /etc/nginx/nginx.conf without touching other files under /etc/nginx/).
volumeMounts:
- name: config-vol
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf # mount only this file, don't overwrite whole directoryIt looks convenient, but the cost is: with subPath, the file will never hot reload .
Reason: subPath copies the file directly to the target path, bypassing the ..data symlink system. No matter how kubelet later switches symlinks, the copied file is unrelated — it stays at the state from the moment of mount.
This trap is dangerous because it fails silently. Everything appears normal, ConfigMap updates apply successfully, but the file never changes. Many engineers waste hours debugging this.
✅ Want hot reload? Don't use subPath If the file must hot reload, mount the whole directory (even if it contains only that one file). If you truly need subPath's precise insertion, accept that it won't hot reload — pair config changes with a rolling restart (using the checksum annotation trick from part 1), which is actually clearer and more controllable.
4. Advanced: projected Volume Merges Multiple Sources
Sometimes a container needs to read: a business config (ConfigMap), TLS certs (Secret), pod metadata (Downward API), and a service account token. The previous approach requires four volumes and four mount paths — verbose and scattered YAML. projected volume solves this by projecting multiple sources into the same mount point :
volumes:
- name: all-in-one
projected:
sources:
- configMap:
name: app-config
- secret:
name: tls-cert
- downwardAPI:
items:
- path: labels
fieldRef:
fieldPath: metadata.labels
- serviceAccountToken:
# token with expiration and audience binding
path: token
expirationSeconds: 3600
audience: my-apiAfter mounting, /etc/all/ contains app.conf, tls.key, labels, token side by side.
The serviceAccountToken deserves special mention. It is not a long-lived legacy token but a short-lived token with expiration and bound audience ; kubelet renews it automatically on expiry. Service meshes and scenarios requiring proof of identity to a specific API rely on this — far more secure than the default mounted SA token.
5. Hard Conclusions from This Article
ConfigMap / Secret mounted as volumes: each key becomes a file in the directory .
Directory mounts hot reload via ..data symlink atomic switch ; applications never read partial data.
Hot reload has latency (sync period + cache TTL), worst case 1–2 minutes — be patient.
subPath mount never hot reloads because it bypasses the symlink mechanism — mount the whole directory if you need hot reload.
File hot reload ≠ application re-reads; the app must watch the file itself; if not, use rolling restart.
Multiple sources (config + certs + token + metadata) into one directory: use projected.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
