Skip to main content

The short version

  • Linux. Containers are a Linux kernel feature. macOS and Windows can run the engine in a VM, which works for development but reintroduces the virtualisation this package exists to avoid.
  • Node 22 or newer.
  • Podman or Docker, reachable over a unix socket.
  • No /dev/kvm. It is not used, and its absence is the reason this package exists.

Podman, rootless

Rootless Podman is the recommended engine. The container’s root is an unprivileged user on the host, through a user namespace, which is the single largest reduction in blast radius available.
On a current Ubuntu or Debian image, subuid/subgid, cgroups v2 delegation and user namespaces are already configured.

Docker

Docker works and needs no setup beyond being installed and running. Be aware of what you are accepting: the Docker daemon runs as root, and membership of the docker group is equivalent to root, because anyone who can reach the socket can bind-mount / into a container. For a service whose entire purpose is running model-generated commands, that is the single privilege most worth not having. Prefer rootless Podman, or rootless Docker, where you can.

Verify, rather than assume

The failure that costs the most time is a limit that is accepted and silently ignored. Prove the host enforces one:
A figure equal to the host’s total RAM means cgroup delegation is missing: the limit was taken and dropped. Treat it as a failure, not a warning — every sandbox on that host is unbounded. Check delegation directly:
You want cpu memory pids in the output. Without them, rootless containers cannot enforce per-sandbox limits at all. From Node, the same question in one call:

What the host needs, and why

Where the socket is found

socketPath is resolved in this order, and never written out:
  1. $DOCKER_HOST, with any unix:// prefix stripped
  2. On Linux, /run/user/<your uid>/podman/podman.sock
  3. /var/run/docker.sock
Podman’s socket is per-user and carries the caller’s own uid. On a host whose service account is 1001 it is /run/user/1001/..., not the /run/user/1000/... that every tutorial prints — which is why it is derived rather than hard-coded. Override it when you need to:

Images

Images are pulled on demand the first time a runtime is asked for, because an image missing from the cache is normal on a fresh host, after a prune, or the first time a different runtime is requested — and failing there would look like a broken installation. Pass image to override, or images on the constructor to change the mapping. Note that rootless Podman keeps images per user, under ~/.local/share/containers: an image pulled with sudo podman is invisible to your service account, and vice versa.