← posts / infrastructure

Swapping Docker Desktop for Apple's container: one VM per container on your Mac, what runs unchanged from Docker Hub, and where you still need Docker

Apple's container CLI has been stable since 1.0 in June and is at 1.5.0. A hands-on swap: install, build, run Postgres from Docker Hub, reach it by name, and the gaps (no Compose, slower cold starts, memory that is not handed back) that decide whether Docker stays.

if.codesOct 19, 2026 · 5 min read#docker#macos#containers#devtoolsAI-assisted

Mac developers are posting about replacing Docker Desktop with Apple's own container CLI. The tool shipped a stable 1.0 on June 9, 2026, and the current release is 1.5.0 (September 29). It runs Linux containers from ordinary OCI images, so most of what you pull from Docker Hub works, but it does so differently: every container gets its own lightweight virtual machine instead of sharing one big Linux VM.

This post walks through a swap you can try in about 15 minutes on an Apple silicon Mac: install, build an image, run Postgres from Docker Hub with a persistent volume, reach containers by name, and then the list of things that will send you back to Docker.

How it differs from Docker Desktop

Docker Desktop starts one Linux VM and runs all your containers inside it, separated by namespaces. Apple's container creates a small VM per container. Two consequences follow:

A conceptual representation of the one-VM-per-container architecture.
A conceptual representation of the one-VM-per-container architecture.
  • Isolation. To escape a container, code has to get past a hypervisor, not only kernel namespaces. That is a real gain if you run code you did not write, such as a coding agent's output or a dependency you have not audited.
  • Each container has its own resources. By default a container gets 1 GB of RAM and 4 CPUs; change that per container with --memory and --cpus. The image builder runs with 2 CPUs and 2048 MB by default.

Requirements: Apple silicon and macOS 26. Intel Macs are not supported, and the maintainers say they will not address issues that cannot be reproduced on macOS 26.

Install and start

Download the installer package from the releases page, install it, then start the system service:

Setting up the container CLI on Apple silicon hardware.
Setting up the container CLI on Apple silicon hardware.
container system start

On first start it offers to install the recommended Linux kernel (a Kata Containers build). Answer Y. Check that everything is up:

container ls --all

An empty table is the right answer. container --help lists every command; most have the Docker-style names you expect (run, build, exec, logs, stop).

Build and run an image

This is the example from Apple's own tutorial: a Python web server that serves one HTML page. Save it as 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"]

Build it, then run it in the background:

container build --tag web-test --file Dockerfile .
container image list
container run --name my-web-server --detach --rm web-test

Each container gets its own IP address on a host network. container ls shows it; open that address in the browser (the tutorial's example is http://192.168.64.3). You can also publish a port to localhost the Docker way:

container run --name web2 --detach --rm --publish 127.0.0.1:8080:80 web-test
curl http://127.0.0.1:8080

The usual inspection commands work as you would guess:

container logs --follow my-web-server
container exec --tty --interactive my-web-server sh
container stats my-web-server
container stop my-web-server web2

Postgres from Docker Hub, with a volume

The official Postgres image runs unchanged. Use a named volume rather than a bind mount: the project's docs say named volumes give better I/O than bind mounts. Set PGDATA to a subdirectory, the standard workaround when the mounted volume root is not empty.

container volume create pgdata
container run --name pg --detach \
  --env POSTGRES_PASSWORD=dev \
  --env PGDATA=/var/lib/postgresql/data/pgdata \
  --volume pgdata:/var/lib/postgresql/data \
  --publish 127.0.0.1:5432:5432 \
  --memory 2g \
  docker.io/library/postgres:17

Check it from inside the container:

container exec pg psql -U postgres -c 'select version();'

Your app on the Mac connects to 127.0.0.1:5432 as before. Stop and delete the container and the data stays in the volume; container volume list shows it.

One difference from Docker to know before you script cleanup: anonymous volumes are not removed when a container started with --rm exits. And container volume prune deletes unused volumes and their contents immediately, with no way back.

Need the backend underneath done properly?I build and harden Go and Python backends. The estimate is free.

Reach containers by name

To let containers find each other (and let you open http://my-web-server.test), set a local DNS domain. Add this to ~/.config/container/config.toml:

An illustration of container networking and DNS discovery.
An illustration of container networking and DNS discovery.
[dns]
domain = "test"

Then restart the service and register the domain with macOS (this step needs sudo):

container system stop
container system start
sudo container system dns create test

Containers on the default network can now reach pg.test and my-web-server.test. On custom networks (container network create foo --subnet 192.168.100.0/24), the docs note that bare-hostname lookup is not supported yet; use IP addresses there.

A persistent dev box: container machine

Version 1.0 added container machine, a long-lived Linux environment rather than a throwaway container. It maps your macOS user and home directory into Linux, so a command run under ~/code/app on the Mac runs in the same directory inside the machine.

container machine create alpine:latest --name dev
container machine run -n dev
container machine ls
container machine stop dev

Apple's docs are blunt about the trade-off: a machine gives up much of the isolation of a regular container, and root inside it can write anywhere in your home directory with your user's permissions. Only create machines from images you trust, and do not use one as a sandbox for agent-generated code. Use a plain container for that.

Where you still need Docker

  • Compose. There is no Compose support. A multi-service app is one docker compose up on Docker Desktop and a set of container run commands (or a shell script) here. Cloud Native Now reports that the GitHub discussion asking for it has more than 150 replies and no timeline from Apple.
  • Cold starts. A VM per container costs start-up time. Benchmarks cited by Cloud Native Now found Docker Desktop roughly 4x faster at cold start, while Apple's tool came out about 33% ahead on memory throughput. If your test suite starts dozens of short-lived containers, measure before switching.
  • Small-file I/O. The same report says small-file I/O lags because of the per-container filesystem model. Large node_modules trees on bind mounts are where you will feel it; prefer named volumes.
  • Memory is not handed back. The resource docs say memory a container frees is not automatically returned to macOS; under pressure macOS swaps, and performance becomes limited by disk. Give each container a realistic --memory rather than a generous one.
  • Kubernetes and the Docker ecosystem. The k8s subcommand is experimental (1.5.0 removed container k8s start for now), and tools that talk to the Docker socket, such as Testcontainers or IDE integrations, expect Docker's API.
  • Intel Macs and older macOS. Not supported.

Should you switch?

For running a database, a cache and a service or two while you code, container does the job with the images you already use, and the per-container VM is a better boundary for untrusted code. If your daily loop depends on Compose files, the Docker socket or many short-lived containers in tests, keep Docker Desktop and try container for the isolated cases. The two can sit side by side on the same Mac while you decide.

Sources

if.codesI build RAG, AI integrations and agent pipelines on Go and Python backends — and write about it here.
// keep reading

More posts on AI and backends.

// free quote

Read something you need? I’ll quote it for free.

RAG, AI integrations, agents or the backend underneath — tell me what you have and what should change. I read every request myself.

Free · no commitment

Tell me what you have. I’ll tell you what it takes.

1Describe the projectA few sentences is enough — about two minutes.
2I review itI read it myself and may ask a follow-up question.
3You get a free quoteScope, approach and estimate — yours to keep, no strings.
What kind of project is it?
Free and without obligation. Your details are used only to reply — see the privacy policy.