This post is a follow up to the great thread: How to get Docker running on Sprites?
Disclaimer: I do understand the post below. I checked it. But (obviously
I didn’t write it. It’s written by Claude-AI. But we also tested this setup. I am using this as we speak.
Remark by the “human-in-the-loop” old-school dev: Yes this feels like docker inside docker inside docker inside an unbelievable awesome genius fly io custom docker without docker in side a docker … ![]()
So yes it’s maybe an unusual, complex setup. But some people might want to run their “golden-tarball” (how a docker-image was called 20 years ago) before going to PROD…
Running containers (not just docker ps) inside a Sprite
Installing Docker and starting dockerd by hand works, as shown above. But on my Sprite (Ubuntu 26.04, kernel 6.12-fly),
the first docker run failed in two ways:
- cgroups:
runc create failed: unable to apply cgroup configuration: cannot enter cgroupv2 "/sys/fs/cgroup/docker" with domain controllers -- it is in an invalid state. Inside the Sprite,/sys/fs/cgroupis a namespaced
threaded cgroup that already contains the Sprite’s own processes, so no child cgroup can get the memory/io
controllers. Moving processes into a leaf cgroup (the usual docker-in-docker trick) fails with
Operation not supported. - Storage:
dmesgshowsoverlayfs: maximum fs stacking depth exceeded. The Sprite’s/is already an overlay.
Docker 29 defaults to the containerd image store, which lives in/var/lib/containerdon that overlay.
What fixes both:
- Sprites already mount a dedicated ext4 volume at
/var/lib/docker(check withfindmnt /var/lib/docker).
Use the classicoverlay2driver so everything lives on that volume. - Run containers with crun with its cgroup manager disabled.
Setup
sudo apt-get update
sudo apt-get install -y docker.io docker-compose-v2 crun
sudo usermod -aG docker "$(whoami)" # $USER is empty in a Sprite shell
/etc/docker/daemon.json:
{
"features": { "containerd-snapshotter": false },
"storage-driver": "overlay2",
"default-runtime": "crun-nocg",
"runtimes": { "crun-nocg": { "path": "/usr/bin/crun", "runtimeArgs": ["--cgroup-manager=disabled"] } },
"bridge": "none",
"iptables": false,
"ip6tables": false,
"live-restore": true,
"log-driver": "local",
"log-opts": { "max-size": "10m", "max-file": "3" }
}
bridge: none and iptables: false are optional: I only use --network host, so Docker doesn’t need to touch
iptables or the forwarding sysctl (which is off on a Sprite).
There’s no systemd, so run dockerd as a Sprite service. It then starts at boot and restarts if it crashes:
sprite-env services create docker --cmd sudo --args /usr/bin/dockerd
sudo docker info | grep -E 'Storage Driver|Backing Filesystem|Default Runtime'
# Storage Driver: overlay2 / Backing Filesystem: extfs / Default Runtime: crun-nocg
If a web server in front needs the containers, let it depend on that service, e.g.
sprite-env services create web --cmd /usr/sbin/nginx --needs docker --http-port 8080.
Test
sudo docker run --rm --network host hello-world
A locked-down container works too: --read-only --tmpfs /tmp --cap-drop ALL --security-opt no-new-privileges:true --user 1001:1001. Inside it, /proc/self/status shows CapEff: 0, NoNewPrivs: 1, Seccomp: 2. Outbound IPv6
works from a --network host container. --restart unless-stopped works too: I killed the container’s process and
Docker started it again.
Caveats
- No resource limits. With cgroups disabled,
--memory/--pids-limitare accepted but not enforced.
Limit the app itself, e.g. a fixed-Xmxfor the JVM. - Checkpoints only capture the Sprite’s writable overlay.
/var/lib/dockeris a separate volume, so don’t rely
on a checkpoint restore to roll back images; pull them again. --network hostonly. Without a bridge, there’s no port publishing (-p).- Cold wake: [result pending]
- Docker Hub’s anonymous pull limits apply as usual.
Tested 2026-11-04 with Docker 29.1.3 (Ubuntu docker.io), containerd 2.2.2 and crun 1.21.