So You Want to Contain Multitudes
Two days ago I decided to try out Apple's own container tool, which enables Apple Silicon Macs to run GNU/Linux in a container. I did this because I really wanted to try Claude Code's --dangerously-skip-permissions mode, but didn't want to automatically clean out my home directory either. Things mostly went ok, but there are caveats!
Getting Started
I followed the tutorial for the 0.7.1 release. It even took me a while to figure out that I wanted to download the container-installer-signed.pkg file from Apple's GitHub releases page.
After you've downloaded it, you should do the typical MacOS developer-installing-programs things and then verify that you have it:
$ which container
/usr/local/bin/container
If you do, you can then start the service:
$ container system start
Verifying apiserver is running...
No default kernel configured.
Install the recommended default kernel from [https://github.com/kata-containers/kata-containers/releases/download/3.17.0/kata-static-3.17.0-arm64.tar.xz]? [Y/n]:
The first time around it will ask you to install some kata kernels, so answer yes to continue.
At any time, you can check on the status of the service. If it started successfully, you'll see something like this:
$ container system status
apiserver is running
application data root: /Users/mshonle/Library/Application Support/com.apple.container/
application install root: /usr/local/
container-apiserver version: container-apiserver version 0.7.1 (build: release, commit: 420be74)
container-apiserver commit: 420be748f18afc685d11987ac5118c928e174c19
DNS Magic
Apple's tutorial covers how you can use a custom DNS name to help you access containers. Here's how to create one called test:
$ sudo container system dns create test
test
$ container system property set dns.domain test
That way, if you have a container named my-cool-django-blog you can access it via Safari as http://my-cool-django-blog.test.
Example Server
Apple gives this nice example. Create a directory and cd into it:
$ mkdir web-test
$ cd web-test
And then create a Dockerfile there so you end up with this:
$ cat Dockerfile
FROM docker.io/python:alpine
WORKDIR /content
RUN apk add curl
RUN echo '<!DOCTYPE html><html><head><title>Hello</title></head><body><h1>Hello, world!</h1></body></html>' > index.html
CMD ["python3", "-m", "http.server", "80", "--bind", "0.0.0.0"]
This uses the Alpine Docker Official Image (DOI), where Alpine is a lightweight Linux (and that is the first time I've written "Linux" in 2026 without calling it "GNU/Linux"). Apple's container can use these Dockerfiles.
Build the Server
Even the baby server example might take several tries (read on to the troubleshooting section if this is you). Eventually, I tried this command and it built the Alpine container for me:
$ container build --tag web-test --file Dockerfile .
[+] Building 9.4s (7/7) FINISHED
=> [resolver] fetching image...docker.io/library/python:alpine 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> oci-layout://docker.io/library/...
=> [linux/arm64/v8 1/4] WORKDIR /content 0.2s
=> [linux/arm64 2/4] RUN apk add curl 3.0s
=> [linux/arm64 3/4] RUN echo '<!DOCTYPE html><html><head><title>Hello</title></head><body><h1>Hello, world!</h1 0.0s
=> exporting to oci image format 0.4s
=> => exporting layers 0.2s
=> => exporting manifest sha256:3680f7b7bf80274e185d6c5558d74e9dfcd207863190f50ae438cabbe8c14acf 0.0s
=> => exporting config sha256:b5814728734c0d9b14b0f5b2c587e0bcd2ea19695dd9be9847f45cb87b13e8f5 0.0s
=> => exporting manifest list sha256:7b9069d719ea31629c473c71e86c36362704f931237517d73c0814243d70195e 0.0s
=> => sending tarball 0.2s
Successfully built web-test:latest
Once a build works, you can see it listed:
$ container image list
NAME TAG DIGEST
python alpine 7af51ebeb83610fb69d633d5...
web-test latest 7b9069d719ea31629c473c71...
Start the Server
After a short while, this command that I ran next returned:
$ container run --name my-web-server --detach --rm web-test
my-web-server
And it left something around:
$ container ls
ID IMAGE OS ARCH STATE ADDR CPUS MEMORY
my-web-server web-test:latest linux arm64 running 192.168.64.3 4 1024 MB
buildkit ghcr.io/apple/container-builder-shim/builder:0.7.0 linux arm64 running 192.168.64.2 2 2048 MB
It's at this point the DNS magic can kick in:
$ open http://my-web-server.test
Never thought seeing a "Hello, world!" could be that interesting!
(Note how that sample server only has 1 GiB of memory! To do more interesting things, you'll need to increase that.)
You can poke around the server by opening a shell:
$ container exec --tty --interactive my-web-server sh
/content # whoami
root
/content # ls -l
total 4
-rw-r--r-- 1 root root 97 Jan 6 23:04 index.html
/content # uname -a
Linux my-web-server 6.12.28 #1 SMP Tue May 20 15:19:05 UTC 2025 aarch64 Linux
/content # exit
It put us into the /content directory because that's what the Dockerfile specified.
Troubeshooting
Sometimes when trying to build you will see "Dialing"... when the container service gets stuck, the first thing you should try is stopping it:
container system stop
If that still hangs, you can find and kill the processes:
$ ps ax -o "pid,comm" | grep Virtualization
99584 /System/Library/Frameworks/Virtualization.framework/Versions/A/XPCServices/com.apple.Virtualization.VirtualMachine.xpc/Contents/MacOS/com.apple.Virtualization.VirtualMachine
After you find the PID (e.g., 99584), then you can -9 it (it's like 67, I think):
kill -9 99584
If your build terminal is still hanging, you can kill the services:
$ killall container-apiserver; killall container-runtime-linux; killall container-core-images; killall container-network-vmnet
You can list your containers and remove the ones you no longer need, e.g.,:
$ container list --all
...
$ container rm 11709408-8d0d-4fab-8f52-ffad87032967
11709408-8d0d-4fab-8f52-ffad87032967
After everything is killed, you can bring it back up:
$ container system start
Verifying apiserver is running...
Putting Together All The Things
It took me a while to figure out:
- How to increase the memory of the containers
- How to get my Claude Code OAuth token for use in the container
- How to get Claude Code installed on the container
- How to execute Claude Code in the container and let it live happy permission-skipping in a dangerous manner
I put it all up on a GitHub repo for you to use: Deeclaud. If you check it out, you can build your own Claude Code running GNU/Linux container with a script (co-authored by Microsoft Copilot, Claude Code, and me):
./manage-container.sh setup
Once that works, you can then run the server containing Claude Code with a special git worktree that gets mounted on the container side:
./deeclaud.sh ../some/path/to/my-cool-repo a-branch-name
The worktree is a copy of your Git repository, which works much better than having a single repository with multiple branches. Multiple, isolated worktrees are great for running multiple parallel tasks on the same project.
(This was the way we used to do things with SCCS/Teamware at Sun, and the same thing BitKeeper did, which is what the the GNU/Linux creator used before he decided to make his own source control system. It took ten years after that before Git had effective worktrees; which were effective in the sense that it was better than making a complete copy of all of the .git internal directories.)
So, how is it? Well, on my 16 GiB M1 MacBook Pro, there is a noticeable slowdown. I think if someone has a newer Mac with an M3+32 GiB of RAM (or better) they should definitely try it out and share their findings with me!
I think if I had this set up in place when Anthropic had the December compute gift credits I would have been able to use more of them!
Macneil Shonle’s Blog