You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: create-symlinks and update-ldcache hooks fail container create when the container rootfs is read-only #2131
Both create-symlinks and update-ldcache write into the container root filesystem. When that filesystem is read-only, the write fails with EROFS, the hook exits 1, and container creation is aborted:
This is the same class of problem as #2026, which was fixed in v1.20.1 for update-application-profile by ignoring read-only filesystem errors. The other two hooks still abort.
Generalized: a CDI hook whose only effect is to modify the container root filesystem should not prevent the container from starting when that filesystem is read-only.
Making both /etc and /usr/lib64 writable is required; making either one writable on its own still fails. Those are the two paths the generated spec's hooks write to:
Kubernetes workloads that set readOnlyRootFilesystem: true cannot use GPUs. This is a common hardening requirement, and it is enforced by policy on many clusters (for example OpenShift restricted-v2 style baselines and Gatekeeper/Kyverno psp-readonlyrootfilesystem constraints), so it is not something the workload owner can simply turn off.
Environment
nvidia-container-toolkit v1.19.0, also reproduced on v1.17.8 with regenerated CDI specs
CRI-O and podman/crun, OpenShift 4.x, NVIDIA GPU Operator v26.3.0
Driver 595.58.03, H100 NVL
Skipping the ldcache update does not make the libraries unreachable: they are bind-mounted into directories glibc already searches by default.
I have a patch following the same approach as the #2026 fix and will open a PR.
Result is identical on v1.17.8 and v1.19.0, so this is not a regression introduced by the 1.18 update-ldcache change:
hook
writable root
read-only root
create-symlinks
exit 0
exit 1
update-ldcache
exit 0
exit 1
# create-symlinks, v1.19.0 and v1.17.8
failed to create link [libcuda.so.1 /usr/lib64/libcuda.so]: failed to create symlink:
symlink libcuda.so.1 .../rootfs/usr/lib64/libcuda.so: read-only file system
# update-ldcache, v1.19.0
Error updating ldcache: error mounting /proc: mkdirat proc: read-only file system
# update-ldcache, v1.17.8
Error updating ldcache: error mounting /proc: error creating directory: mkdir .../rootfs/proc: read-only file system
Note the update-ldcache failures above are from the released binaries, which create proc in the container root. main no longer does that (maskPseudoFilesystems skips a missing proc), so on main the first read-only write is ensureLdsoconfFile/createLdsoconfdFile under /etc. Both are Go-level writes wrapped with %w, which is what #2132 keys off.
Why this is not reported more widely
Whether the hooks ever see a read-only rootfs depends on the runtime, not on the workload. On a stock OpenShift node the createContainer hooks run before the read-only remount is applied, so the hooks write into the still-writable upperdir and a readOnlyRootFilesystem: true GPU pod starts normally.
Measured on OCP 4.19.26 / CRI-O 1.31.13 / crun 1.26 / driver 580.82.07, toolkit v1.17.8, CDI forced to mode = "cdi". A single pod shows both facts at once: /usr/lib64/libcuda.so exists (the hook created it) while /proc/mounts reports overlay / overlay ro and touch is denied on /, /etc and /usr/lib64.
Where the rootfs is read-only at hook time, container creation aborts, and the hooks are then the only thing standing between a hardened workload and a working GPU. That asymmetry is the argument for tolerating EROFS rather than requiring every affected cluster to drop the hardening.
Root cause update, superseding my earlier analysis above.
The hooks are not at fault. crun before 1.26 remounted the rootfs read-only before running createContainer hooks, while runc does it after; NVIDIA filed this as containers/crun#1924 and it was fixed in containers/crun#1925 (crun 1.26, 2025-12-22). On a runtime that applies the remount after the hooks, a readOnlyRootFilesystem: true GPU pod starts fine with CDI hooks running, confirmed on a crun 1.26 node.
Two corrections to what I wrote earlier:
"The libraries remain reachable" is wrong. create-symlinks creates the SONAME -> library links for the injected driver libraries, so on a read-only root nothing can resolve libcuda.so.1 regardless of image layout. Tolerating EROFS would start a container whose GPU is unusable and move the failure to the first CUDA call.
A hook-side fallback (bind-mounting a generated ld.so.cache) does not help either: ldconfig records dir/soname and relies on the symlink it creates, so with a read-only library directory every entry dangles.
#2132 is therefore reduced to diagnostics: the hooks keep failing container creation, but the error now names the read-only rootfs and the runtime requirement (runc or crun >= 1.26) instead of a bare exit code 1.
For reference, OpenShift ships crun 1.23.1 through 4.20.16 and crun 1.26 from 4.20.17.
Activity
sahanasreenath commentedon Oct 10, 2026
Additional data: a reproduction that needs no GPU, no container runtime and no CDI spec, which isolates the hooks from the runtime.
Create a bundle whose root is a genuine read-only mount and invoke the hook directly with the OCI state on stdin:
Result is identical on v1.17.8 and v1.19.0, so this is not a regression introduced by the 1.18
update-ldcachechange:create-symlinksupdate-ldcacheNote the
update-ldcachefailures above are from the released binaries, which createprocin the container root.mainno longer does that (maskPseudoFilesystemsskips a missingproc), so onmainthe first read-only write isensureLdsoconfFile/createLdsoconfdFileunder/etc. Both are Go-level writes wrapped with%w, which is what #2132 keys off.Why this is not reported more widely
Whether the hooks ever see a read-only rootfs depends on the runtime, not on the workload. On a stock OpenShift node the
createContainerhooks run before the read-only remount is applied, so the hooks write into the still-writable upperdir and areadOnlyRootFilesystem: trueGPU pod starts normally.Measured on OCP 4.19.26 / CRI-O 1.31.13 / crun 1.26 / driver 580.82.07, toolkit v1.17.8, CDI forced to
mode = "cdi". A single pod shows both facts at once:/usr/lib64/libcuda.soexists (the hook created it) while/proc/mountsreportsoverlay / overlay roandtouchis denied on/,/etcand/usr/lib64.Where the rootfs is read-only at hook time, container creation aborts, and the hooks are then the only thing standing between a hardened workload and a working GPU. That asymmetry is the argument for tolerating
EROFSrather than requiring every affected cluster to drop the hardening.sahanasreenath commentedon Oct 10, 2026
Root cause update, superseding my earlier analysis above.
The hooks are not at fault. crun before 1.26 remounted the rootfs read-only before running
createContainerhooks, while runc does it after; NVIDIA filed this as containers/crun#1924 and it was fixed in containers/crun#1925 (crun 1.26, 2025-12-22). On a runtime that applies the remount after the hooks, areadOnlyRootFilesystem: trueGPU pod starts fine with CDI hooks running, confirmed on a crun 1.26 node.Two corrections to what I wrote earlier:
create-symlinkscreates theSONAME -> librarylinks for the injected driver libraries, so on a read-only root nothing can resolvelibcuda.so.1regardless of image layout. Tolerating EROFS would start a container whose GPU is unusable and move the failure to the first CUDA call.ld.so.cache) does not help either: ldconfig recordsdir/sonameand relies on the symlink it creates, so with a read-only library directory every entry dangles.#2132 is therefore reduced to diagnostics: the hooks keep failing container creation, but the error now names the read-only rootfs and the runtime requirement (runc or crun >= 1.26) instead of a bare exit code 1.
For reference, OpenShift ships crun 1.23.1 through 4.20.16 and crun 1.26 from 4.20.17.