Running Docker Inside Apple Container
After upgrading my M3 MacBook Air to macOS 26, I finally decided to give Apple Container a try.
For a while I've been avoiding container technology on macOS for two main reasons:
- Most of my development work happens on remote Linux VMs.
- The majority of technologies that run Linux containers on macOS actually operate inside a VM powered by Apple's Virtualization Framework, which isn't ideal from a battery preservation perspective since it requires a VM running constantly.
Apple Container is particularly interesting because each container is sandboxed by a microVM. The key advantage of microVM-level isolation is that each Linux container has full privileges inside the microVM, effectively running as a complete Linux system. This enables capabilities like systemd and even Docker itself to run inside containers.
Given these capabilities, I decided to give running Docker inside a container a spin, for fun and profit.
Getting Started with Container on macOS
I installed container
using Mise:
mise use -g container
# Start the container system
container system start
What's remarkable about container
is that it requires no admin or sudo access at all. The installation is done entirely in user space without requiring any special permissions.
Building the Container Image with Docker and Systemd
Here is my Dockerfile:
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y \
systemd \
curl \
ca-certificates \
openssh-server \
iproute2 \
dnsutils \
iptables \
&& install -m 0755 -d /etc/apt/keyrings \
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc \
&& chmod a+r /etc/apt/keyrings/docker.asc \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \
&& apt-get update \
&& apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin \
&& update-alternatives --set iptables /usr/sbin/iptables-legacy \
&& update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["/sbin/init"]
Several important aspects of this Dockerfile:
Systemd is installed to enable the container to run as a fully-fledged Linux system.
iptables legacy mode is required because Docker doesn't properly support the latest nftables implementation. The update-alternatives
commands ensure the legacy version is used.
Docker installation follows the official Ubuntu installation guide.
The /sbin/init
entrypoint ensures systemd starts as PID 1 when the container launches.
Using the Built-in Buildkit Backend
Apple Container includes a buildkit backend, which you can view using the container ls
command:
$ container ls
ID IMAGE OS ARCH STATE ADDR
buildkit ghcr.io/apple/container-builder-shim/builder:0.6.0 linux arm64 running 192.168.64.2
Building the image is straightforward:
container build -t ubuntu:24.04-docker .
The build command syntax mirrors docker build
, except you use container build
instead.
Running the Container
Launch the container using the container run
command:
container run -it --name ubuntu-docker --rm ubuntu:24.04-docker
The container will start and display console logs showing systemd initialization:
systemd 255.4-1ubuntu8.10 running in system mode (+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT -GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY +P11KIT +QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -BPF_FRAMEWORK -XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified)
Detected virtualization container-other.
Detected architecture arm64.
Welcome to Ubuntu 24.04.3 LTS!
Queued start job for default target graphical.target.
[ OK ] Created slice system-getty.slice - Slice /system/getty.
[ OK ] Created slice system-modprobe.slice - Slice /system/modprobe.
[ OK ] Created slice user.slice - User and Session Slice.
... (more lines omitted) ...
Starting systemd-update-utmp-runlevel.service - Record Runlevel Change in UTMP...
[ OK ] Finished systemd-update-utmp-runlevel.service - Record Runlevel Change in UTMP.
Ubuntu 24.04.3 LTS ubuntu-docker console
ubuntu-docker login:
To execute commands inside the running container:
$ container exec -it ubuntu-docker /bin/bash
root@ubuntu-docker:/# docker run -it --rm ubuntu:24.04 bash
Unable to find image 'ubuntu:24.04' locally
24.04: Pulling from library/ubuntu
7bdf644cff2e: Pull complete
Digest: sha256:fdb6c9ceb1293dcb0b7eda5df195b15303b01857d7b10f98489e7691d20aa2a1
Status: Downloaded newer image for ubuntu:24.04
root@b03bde3abfde:/# apt-get update
Get:1 http://ports.ubuntu.com/ubuntu-ports noble InRelease [256 kB]
Docker runs successfully inside the container, demonstrating the full capabilities of the microVM isolation.
Potential Use Cases
This is my early exploration of Apple Container, and it represents an interesting piece of technology with several potential applications:
On-demand sandboxed environments for isolating LLM-generated code execution on Apple Silicon, providing security through microVM isolation.
Lightweight Linux environments on macOS without the overhead of traditional VMs, potentially serving as an alternative to tools like VMware Fusion.
It's worth noting that Apple might leverage this technology to provide a WSL-equivalent experience on macOS in the future, bringing Linux as a subsystem to macOS with more seamless integration.