Skip to content

nerdctl run can't use the CimFS snapshotter on Windows (ctr can) #5065

Description

@inistor

Summary

nerdctl --snapshotter cimfs run … fails on Windows even though ctr --snapshotter cimfs run … works against the same containerd. nerdctl host-mounts the image rootfs during run setup, and containerd's Windows mount helper only supports windows-layer mounts.

Repro

Windows 11 25H2 (build 26200), containerd v2.3.1 configured with the cimfs differ/unpack_config (so pulls unpack fine):

nerdctl --snapshotter cimfs pull mcr.microsoft.com/windows/nanoserver:ltsc2025   # OK
nerdctl --snapshotter cimfs run --rm mcr.microsoft.com/windows/nanoserver:ltsc2025 cmd /c "echo ok"
# FATA[...] invalid windows mount type: 'CimFS'

The same image runs fine via ctr --snapshotter cimfs run ….

Cause

generateMountOpts (pkg/cmd/container/run_mount.go) always takes a snapshotter.View of the image rootfs and host-mounts it to a temp dir — mount.All(mounts, tempDir) on Windows — to seed image-VOLUME / anonymous-volume contents. For a cimfs snapshot the mount type is CimFS, and containerd's core/mount/mount_windows.go Mount.mount() rejects anything that isn't windows-layer:

if m.Type != "windows-layer" {
    return fmt.Errorf("invalid windows mount type: '%s'", m.Type)
}

ctr run doesn't do this pre-mount (it hands the snapshot mounts straight to the runhcs shim, which mounts CimFS natively), which is why it works.

Possible fixes

  • nerdctl: skip the View + mount.All pre-mount when there's nothing to seed (image declares no VOLUMEs and no -v/--mount volume copies) — makes the common case (e.g. nanoserver) work immediately. There's already a MountManager().Activate step here that tolerates ErrNotImplemented; the fallback mount.All is what fails.
  • containerd (root fix, benefits everyone): teach core/mount/mount_windows.go to host-mount CimFS/BlockCIM snapshots read-only. Then images with real VOLUMEs work too. Both repos are in the containerd org.

Context

containerd fully supports CimFS on GA Windows (25H2); the daemon-side wiring gap is tracked at containerd/containerd#13782 and the Docker Engine side at moby/moby#53063.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions