Node-local state and the reimage that erases it
A fix applied to a running node survives until that node is reimaged, and then it is gone with no event to connect the two. The decision that prevents it is knowing which changes are node-local and which live on shared storage.
Archive date is an editorial placement, not the original publication date. Published 17 October 2026; documentation reviewed 17 October 2026. Examples describe proposed checks, not customer incidents or measured product results.
A node misbehaves, you fix it, you verify the fix, you move on. Eight weeks later the same node shows the same fault. Nothing changed in between — except that the node was reimaged for a kernel update, and the fix lived in its local filesystem.
This is the most reliably recurring class of operational loss in a provisioned estate, and avoiding it is a classification question asked before the change, not a discipline applied afterwards.
Classify the change before making it
Every configuration change in a cluster lands in one of three places, and the place determines what you have to do to make it durable.
| Where it lives | Survives reimage? | What durability requires |
|---|---|---|
Node-local filesystem (/etc, /var, local units) |
No | bake into the image, or manage it from config management |
| Shared filesystem (parallel FS, NFS-mounted app tree) | Yes | nothing — one edit reaches every node |
| Provisioning system’s own database (node records, roles, kernel params) | Yes | commit it there, not on the node |
The practical test is a single command. If the path is a mount from shared storage, the change is global and already durable:
findmnt --target /the/path -o SOURCE,FSTYPE,TARGET
This matters more than it sounds, because adjacent paths frequently differ. A software tree can be a shared mount while its parent directory is local disk — so an edit one level up is node-local even though everything below it is global. One cluster can have a tool tree on shared storage where another has the same path on local disk, which is exactly how a procedure that worked on one cluster silently fails on the next.
Things that are almost always node-local and get forgotten: /etc/motd and
/etc/motd.d, PAM stacks, sshd config, systemd units and drop-ins, /etc/fstab,
/etc/security/limits.d, cgroup and slice configuration, local firewall rules, node exporter
collectors in /var/lib, and anything written into /etc/profile.d.
Things that are usually shared: the application and module trees on a parallel filesystem, which is why a module fix is a one-time edit and a PAM fix is a fleet operation.
Per-node overrides defeat the category default
The trap that turns a correct bake into a no-op. Provisioning systems let you assign an image at the group or category level, and then let individual nodes override it. Both are normal and both are used.
So you bake your change into the category’s image, verify the file is present in that image, reboot the node — and the change is not there, because the node has its own image assigned and the category default was never consulted.
Enumerate what each node actually resolves to, not what the category says:
for n in login001 login002 node041 node042; do
printf "%-10s category=%-10s image=%s\n" "$n" \
"$(cmsh -c "device use $n; get category" 2>/dev/null)" \
"$(cmsh -c "device use $n; get softwareimage" 2>/dev/null)"
done
Then bake into every image that is actually assigned, plus the category default so that future nodes inherit it. A fleet of four login nodes can easily be four distinct images.
The matching hazard in the other direction: a category’s default image may be shared with a different class of machine. If the login category’s default image is the general compute image, baking a login-specific change into it reaches every compute node too. Check what else uses an image before writing to it:
cmsh -c "softwareimage use <image>; show" 2>/dev/null
cmsh -c "device list" | awk '{print $4}' | sort | uniq -c # nodes per category
Verify from the image, not from the copy
Writing into an image is a file copy into a directory tree, and file copies succeed in ways that do not accomplish the goal — wrong path inside the tree, a stale file still present beside the new one, the right content with the wrong mode.
So read back from the image path and assert on content:
IMG=/cm/images/login-2026-10
F=$IMG/etc/motd.d/userinfo.motd
install -m 644 -o root -g root ./userinfo.motd "$F"
# assertions, from the image tree
printf " files in motd.d : %s (want 1)\n" "$(ls -1 $IMG/etc/motd.d | wc -l)"
printf " legacy motd : %s lines (want 0)\n" "$([ -s $IMG/etc/motd ] && wc -l < $IMG/etc/motd || echo 0)"
printf " content marker : %s (want >=1)\n" "$(grep -c 'sbatch' "$F")"
printf " mode : %s (want 644)\n" "$(stat -c %a "$F")"
The files in motd.d line is the one that catches the real mistake. Baking a new file while an
old one is still in a drop-in directory means the node boots with both, because that kind of
directory uses every file in it. An image is where those leftovers persist indefinitely.
Make the live fix and the baked fix one operation
The reason fixes get lost is that applying them live and baking them are two separate acts, and the second one is the one that gets skipped under time pressure. The fix is to make them the same script, parameterised by target root:
apply() { # apply "" for live, apply /cm/images/<img> for an image
local R=$1
install -D -m 644 ./userinfo.motd "$R/etc/motd.d/userinfo.motd"
: > "$R/etc/motd"
find "$R/etc/motd.d" -name '*.bak*' -exec mv -t "$R/root/motd-backups/" {} +
}
apply "" # the running node
for i in /cm/images/login-*; do apply "$i"; done # every image that provisions one
Run the same assertions against both roots. Then the question “is this durable” has a mechanical answer rather than depending on whether anyone remembered.
A reimage checklist
- Which paths did I change?
findmnt --targeteach one. - For node-local paths: which images provision this node, including per-node overrides?
- Is any of those images shared with a different class of node?
- Apply to the running node and to every image, from one script.
- Assert on content read back from the image tree, not on the copy’s exit status.
- Confirm the drop-in directories in the image contain only what should be there.
- Record which images were touched, so the next person updating one does not drop your change.
Symptom against cause
| Symptom | Cause | Check |
|---|---|---|
| Fix gone after a kernel update | Node-local, never baked | findmnt --target before changing |
| Baked and verified, node still wrong | Node has a per-node image override | resolve per node, not per category |
| Change appeared on unrelated nodes | Image shared across classes | list devices per image before writing |
| Node boots with old and new config | Leftover file in a drop-in dir in the image | count files in the directory |
| Works on one cluster, not the other | Same path, local on one, shared on the other | compare findmnt output |
| Image file present, no effect | Wrong path inside the tree, or wrong mode | assert content and mode from the image |
Durability is a property of location, not of effort. The verification that matters is not that the change was applied, but that it is stored somewhere a rebuild reads from.