I run Docker on macOS without Docker Desktop. Colima runs the daemon in a VM, the Homebrew docker package is the client, and lazydocker is the UI when I want one. No account, no sign-in, no background app, and the whole thing starts and stops from the command line.
This post used to describe installing Docker.app and driving it with a pair of osascript wrappers. That worked in 2020. Colima is better, so this is how I do it now.
Install
brew install colima docker docker-compose docker-buildx docker-credential-helper lazydocker
docker is just the client. Colima provides the daemon. Compose and Buildx are CLI plugins rather than separate binaries, and the credential helper keeps registry logins in the macOS keychain instead of a plaintext file.
Homebrew installs the plugins somewhere the Docker CLI doesn’t look by default, so point it at them in ~/.docker/config.json:
{
"cliPluginsExtraDirs": ["/opt/homebrew/lib/docker/cli-plugins"],
"credsStore": "osxkeychain"
}
/opt/homebrew is what brew --prefix prints on Apple Silicon. If yours prints something else, use that.
Without cliPluginsExtraDirs, docker compose and docker buildx fail as unknown commands while docker-compose still works, which is a confusing way to find out.
Configure the VM
Colima writes ~/.colima/default/colima.yaml on first start and then leaves it alone. Put the file in place before that first start, because several of its settings can’t be changed once the VM exists.
mkdir -p ~/.colima/default
$EDITOR ~/.colima/default/colima.yaml
The settings I care about:
cpu: 4
memory: 8
disk: 60
arch: aarch64
runtime: docker
vmType: vz
mountType: virtiofs
autoActivate: true
kubernetes:
enabled: false
mounts: []
vmType: vz uses Apple’s Virtualization framework instead of QEMU, which needs macOS 13 or newer. mountType: virtiofs is the fast mount driver and requires vz. autoActivate: true makes colima the active Docker context on start.
mounts: [] is deliberate and the empty list matters. An empty list mounts $HOME writable. mounts: null, or leaving the key out entirely, mounts nothing at all.
Then:
colima start
colima status should report vz and virtiofs, and docker context ls should show colima as current.
Changing it later
CPU and memory are adjustable on an existing VM:
colima stop
colima start --cpu 6 --memory 12
Disk can grow but never shrink. arch, runtime, vmType, and mountType are fixed for the life of the VM. Changing one of those means starting over:
colima delete
colima start
lazydocker
lazydocker
It reads the active docker context, so it finds Colima on its own. It needs a real terminal, which is worth knowing before you try to run it from a script. Its config lives at ~/Library/Application Support/lazydocker/config.yml and stays empty until you change something from inside the UI.
Colima doesn’t mount /tmp
Colima mounts $HOME. It does not mount /tmp.
docker run -v /tmp/work:/data alpine ls /data
That prints nothing and exits 0. The bind mount silently resolves to an empty directory inside the VM, and Docker reports no error. I’ve lost time to this more than once. Keep bind mount sources under $HOME:
docker run -v ~/work:/data alpine ls /data