From 3aac57c0ab0359331ace7e22f1cc3641f43ad1af Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Sat, 1 Nov 2025 04:01:24 +0900 Subject: [PATCH] [WIP] Initial support for nerdbox See docs/nerdbox.md Fix issue 4571 WIP: Does not work yet Signed-off-by: Akihiro Suda --- README.md | 1 + docs/nerdbox.md | 50 ++++++++++++++++++++++++++++++++++ pkg/cmd/container/create.go | 14 +++++++--- pkg/cmd/container/run_mount.go | 7 +++-- 4 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 docs/nerdbox.md diff --git a/README.md b/README.md index b0cb1698a95..993142d559b 100644 --- a/README.md +++ b/README.md @@ -298,6 +298,7 @@ Experimental features: - [`./docs/freebsd.md`](./docs/freebsd.md): Running FreeBSD jails - [`./docs/ipfs.md`](./docs/ipfs.md): Distributing images on IPFS - [`./docs/builder-debug.md`](./docs/builder-debug.md): Interactive debugging of Dockerfile +- [`./docs/nerdbox.md`](./docs/nerdbox.md): Running Linux containers on macOS using nerdbox Implementation details: diff --git a/docs/nerdbox.md b/docs/nerdbox.md new file mode 100644 index 00000000000..33759d6bc4d --- /dev/null +++ b/docs/nerdbox.md @@ -0,0 +1,50 @@ +# nerdbox (experimental) + +| :zap: Requirement | nerdctl >= 2.2, containerd >= 2.2 | +|-------------------|-----------------------------------| + + +nerdctl supports [nerdbox](https://github.com/containerd/nerdbox) experimentally +for running Linux containers, including non-Linux hosts such as macOS. + +## Prerequisites +- [nerdbox](https://github.com/containerd/nerdbox) with its dependencies + +- `/var/lib/nerdctl` directory chowned for the current user: +```bash +sudo mkdir -p /var/lib/nerdctl +sudo chown $(whoami):staff /var/lib/nerdctl +``` + +## Usage + +```bash +nerdctl run \ + --rm \ + --snapshotter erofs \ + --runtime io.containerd.nerdbox.v1 \ + --platform=linux/arm64 \ + --net=host \ + --log-driver=none \ + hello-world +``` + +Lots of CLI flags still do not work. + +## FAQ +### How is nerdbox comparable to Lima ? + +The following table compares nerdbox and [Lima](https://lima-vm.io/) +on macOS for running Linux containers: + +| | nerdbox | Lima | +|-----------------------|---------|-------------| +| #VM : #Container | 1:1 | 1:N | +| VM image | minimal | full Ubuntu | +| containerd running on | host | inside VM | +| Low-level runtime | krun | runc | +| Snapshotter | erofs | overlayfs | + +See also: +- https://github.com/containerd/nerdbox +- https://lima-vm.io/docs/examples/containers/containerd/ diff --git a/pkg/cmd/container/create.go b/pkg/cmd/container/create.go index 69e6919ccd3..7afa29192fa 100644 --- a/pkg/cmd/container/create.go +++ b/pkg/cmd/container/create.go @@ -118,9 +118,15 @@ func Create(ctx context.Context, client *containerd.Client, args []string, netMa return nil, nil, err } - opts = append(opts, - oci.WithDefaultSpec(), - ) + if options.Platform == "" { + opts = append(opts, + oci.WithDefaultSpec(), + ) + } else { + opts = append(opts, + oci.WithDefaultSpecForPlatform(options.Platform), + ) + } platformOpts, err := setPlatformOptions(ctx, client, id, netManager.NetworkOptions().UTSNamespace, &internalLabels, options) if err != nil { @@ -303,7 +309,7 @@ func Create(ctx context.Context, client *containerd.Client, args []string, netMa // The OCI hooks we define (whose logic can be found in pkg/ocihook) primarily // perform network setup and teardown when using CNI networking. // On Windows, we are forced to set up and tear down the networking from within nerdctl. - if runtime.GOOS != "windows" { + if runtime.GOOS != "windows" && runtime.GOOS != "darwin" { hookOpt, err := withNerdctlOCIHook(options.NerdctlCmd, options.NerdctlArgs) if err != nil { return nil, generateRemoveOrphanedDirsFunc(ctx, id, dataStore, internalLabels), err diff --git a/pkg/cmd/container/run_mount.go b/pkg/cmd/container/run_mount.go index bd0c4a08645..07f3871a753 100644 --- a/pkg/cmd/container/run_mount.go +++ b/pkg/cmd/container/run_mount.go @@ -194,7 +194,8 @@ func generateMountOpts(ctx context.Context, client *containerd.Client, ensuredIm } } - if runtime.GOOS == "linux" { + switch runtime.GOOS { + case "linux": defer unmounter(tempDir) for _, m := range mounts { m := m @@ -213,7 +214,9 @@ func generateMountOpts(ctx context.Context, client *containerd.Client, ensuredIm return nil, nil, nil, fmt.Errorf("failed to mount %+v on %q: %w", m, tempDir, err) } } - } else { + case "darwin": + // NOP + default: defer unmounter(tempDir) if err := mount.All(mounts, tempDir); err != nil { if err := s.Remove(ctx, tempDir); err != nil && !errdefs.IsNotFound(err) {