container compose

Docker Compose for Apple’s container.

Run your existing compose.yaml files on Apple’s container, unchanged. The plugin adds the Compose that container lacks, running Docker Engine and Docker Compose inside one lightweight Linux machine.

$ brew install RobertDeRose/tap/container-compose-plugin

Free and open source under Apache-2.0. macOS 15 or newer on Apple silicon.

$ compose up -d
[+] Running 5/5
 ✔ Network my-app_default    Created
 ✔ Container my-app-db-1     Started
 ✔ Container my-app-cache-1  Started
 ✔ Container my-app-api-1    Started
 ✔ Container my-app-web-1    Started
$ curl localhost:8080/health
{"status":"ok"}

Everything Compose does, on the runtime Apple ships.

The plugin doesn’t reimplement or translate Compose. It runs the real docker compose in a Linux machine managed by container, and handles the parts that make it feel local.

Your Compose files work as they are

Every subcommand, flag, profile, and file format works, because it is Docker Compose. No conversion step, no unsupported keys.

compose up -d
compose logs -f api
compose exec db psql

Published ports on localhost

TCP ports your services publish appear on 127.0.0.1 and ::1 within seconds, the way Docker Desktop does it. Loopback only, never your LAN.

ports:
  - "8080:80"   # open localhost:8080

A Docker socket for your other tools

Point the docker CLI, your IDE, or Testcontainers at the Docker Engine inside the machine. Fast enough to run in every new shell, and it doesn’t boot the machine until a tool connects.

export DOCKER_HOST="$(compose --socket-path)"
One machine for every project
All projects share a machine named compose. Images, volumes, networks, and build cache survive compose down and restarts.
Your home folder at the same path
Your macOS home directory is mounted read-write at the same path inside the machine, so bind mounts like ./src:/app just work.
Shuts itself off when idle
Set an idle timeout and the machine powers off after Docker goes quiet. The next command boots it again.
Feels native to container
Compose help reads as container compose, and bash, zsh, and fish completions are generated without booting anything.
Never touches your data on its own
The plugin records which machine it created and refuses to adopt one it didn’t. Installing, upgrading, or removing it never deletes the machine.
Nothing installed behind your back
No daemon is registered and no Apple service is replaced or patched. It’s a plugin directory with one executable in it.

How a command runs

A thin client over the public APIs of container. Your first command creates the machine; every later one reuses it.

  1. compose makes sure the plugin is registered with your current container, then hands off to container compose.
  2. container finds the plugin and passes it your arguments.
  3. The plugin checks your working directory is under your home folder and picks the environment to forward.
  4. It starts the background forwarder for ports and the Docker socket, if one isn’t running.
  5. It creates the compose machine on first use, or verifies the existing one is its own.
  6. It boots the machine if needed and waits for Docker.
  7. It runs docker compose inside the machine, relaying input, output, terminal size, and signals.

Your Mac

Terminal
compose up
IDE, Testcontainers
DOCKER_HOST
Browser
localhost:8080

Linux machine compose

Docker Engine
Docker Compose
Buildx

Your home folder, mounted at the same path

How it compares

Docker Desktop and Podman Desktop are full desktop apps that bring their own virtual machine. If you already use Apple’s container, or want the least software running on your Mac, this plugin adds Compose and nothing else.

Feature container compose Docker Desktop Podman Desktop
Cost Free for everyone Paid subscription for larger companies Free for everyone
License Apache-2.0 Proprietary Apache-2.0
What you install A CLI plugin for container A desktop app with background services A desktop app plus the Podman CLI
Virtual machine Apple’s container machine Docker’s own Linux VM A Podman machine
Compose engine Docker Compose on Docker Engine Docker Compose on Docker Engine An external Compose provider against Podman
Ports on localhost TCP TCP and UDP TCP and UDP
Docker API socket Yes, opt-in, private to your user Yes Yes, Docker-compatible
Graphical interface Optional and customizable Yes Yes
Kubernetes Built into container as container k8s Built in Through Kind or Minikube
Platforms macOS on Apple silicon macOS, Windows, Linux macOS, Windows, Linux

Choose container compose if

  • You already run Apple’s container and miss Compose.
  • You want Compose without a desktop app or a license to track.
  • Your projects need exact Docker Compose behavior, not a reimplementation.
  • You want to pick your own dashboard, or work without one.

Choose something else if

  • Your team works across Windows, Linux, and Intel Macs.
  • Your services publish UDP ports to the host.

Install

Pick the route that matches how you installed container. Homebrew and mise also give you the compose command, which re-registers the plugin whenever container is upgraded, so it keeps working.

Homebrew

Installs container too if it’s missing, and adds a shorter compose command that keeps the plugin registered across brew upgrade.

brew install RobertDeRose/tap/container-compose-plugin
container system start
compose --help

mise

Installs container and the plugin with no elevated privileges, and adds the compose command. It registers the plugin with mise’s container on first use, and again after mise upgrade.

mise use -g container 'github:RobertDeRose/container-compose-plugin[bin_path=cli,strip_components=1]'
container system start
compose --help

Apple’s installer

Apple’s package installs under /usr/local, so the installer asks for your password. Download with gh or curl, not a browser: release builds aren’t notarized.

gh release download --repo RobertDeRose/container-compose-plugin --pattern '*-darwin-arm64.tar.gz*'
shasum -a 256 -c container-compose-plugin-*-darwin-arm64.tar.gz.sha256
tar -xzf container-compose-plugin-*-darwin-arm64.tar.gz
container system start
./compose/install.sh

This route doesn’t add the compose command. Type container compose wherever this page says compose, and run the installer again after upgrading container.

From source

Needs Swift 6.2 or newer.

git clone https://github.com/RobertDeRose/container-compose-plugin.git
cd container-compose-plugin
container system start
make install

This route doesn’t add the compose command. Type container compose wherever this page says compose, and run make install again after upgrading container.

Then, from any project in your home folder

cd ~/projects/my-app
compose up -d

The first run pulls the machine image and boots the machine, so it takes longer. After that, commands start in the already-running machine.

Good to know before you start
  • Projects and bind mounts must be under your home folder.
  • All projects share one machine, so give projects distinct names if they would collide.
  • UDP ports aren’t forwarded to localhost.
  • A port another Mac app already uses isn’t forwarded, so localhost reaches that app instead. AirPlay Receiver holds 5000 and 7000; pick other ports or turn it off.
  • Forwarding finds published ports through Docker’s userland proxy, which is on by default. If you disable it, reach services through the machine’s address instead.
  • Your host Docker credentials and SSH agent aren’t reused inside the machine.
  • Deleting the machine with container machine delete removes all Docker data, including volumes.

Add a dashboard if you want one

There’s no built-in GUI, so you choose your own. A web dashboard runs as a Compose project like any other, its page appears on localhost, and Safari’s File → Add to Dock turns it into an app.

Arcane

Arcane manages containers, images, volumes, networks, and Compose projects, closest to the dashboard in Docker Desktop. Source on GitHub, BSD-3-Clause.

Arcane encrypts what it stores with a key of your own. This creates one, and leaves an existing key alone:

mkdir -p ~/arcane && cd ~/arcane
[ -f .env ] || { key=$(openssl rand -hex 32) && printf 'ARCANE_ENCRYPTION_KEY=%s\n' "$key" > .env; }

Save this as ~/arcane/compose.yaml:

services:
  arcane:
    image: ghcr.io/getarcaneapp/manager:latest
    ports:
      - "3552:3552"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - arcane-data:/app/data
    environment:
      APP_URL: http://localhost:3552
      ENCRYPTION_KEY: ${ARCANE_ENCRYPTION_KEY}
      ANALYTICS_DISABLED: "true"   # Arcane sends usage analytics unless this is set
    cgroup: host
    restart: unless-stopped

volumes:
  arcane-data:

Run compose up -d in ~/arcane, open localhost:3552, and sign in as arcane with password arcane-admin. Arcane asks you to change it, and has no setting to skip signing in. Keep ~/arcane/.env with any backup of Arcane’s data: its key decrypts the credentials Arcane stores. For a projects folder on your Mac, more users, or single sign-on, see Arcane’s docs.

Dockge

Dockge focuses on Compose stacks: write, start, stop, and update them, with live logs and a terminal. Each stack stays a plain folder with a compose.yaml, so compose in that folder sees the same project. MIT licensed.

mkdir -p ~/dockge/stacks && cd ~/dockge

Save this as ~/dockge/compose.yaml:

services:
  dockge:
    image: louislam/dockge:1
    ports:
      - "5001:5001"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data:/app/data
      - ${HOME}/dockge/stacks:${HOME}/dockge/stacks
    environment:
      DOCKGE_STACKS_DIR: ${HOME}/dockge/stacks
    restart: unless-stopped

Run compose up -d in ~/dockge, open localhost:5001, and create your admin account. Dockge is reachable only from this Mac, so if you’d rather not sign in, turn it off under Settings → Security → Disable Auth. For everything else, see Dockge’s README.

Other

Anything that speaks the Docker API can manage your Compose projects, through the Docker socket the plugin provides.

Apps on your Mac, such as the docker CLI, lazydocker, VS Code’s Container Tools, JetBrains IDEs, and Testcontainers, connect through DOCKER_HOST. Add this to your shell profile:

export DOCKER_HOST="$(compose --socket-path)"

For an app that has its own Docker connection setting, paste the address compose --socket-path prints. The socket listens once any compose command has run since you logged in, and your shell profile takes care of that.

Web dashboards, such as Portainer or the log viewer Dozzle, run like Arcane and Dockge above: mount /var/run/docker.sock in their compose.yaml and publish their port.

A running dashboard keeps the machine on

If you’ve turned on idle shutdown with idle-shutdown-seconds, a dashboard counts as activity, so the machine never powers off while it runs. Run compose down in the dashboard’s folder when you’re done with it.