Part 2 of 3
Compose Is Missing — Here's What Actually Works
Container Machines v1.0 + a shell script + DNS config = your multi-container setup, running. No compose required.
Compose Timeline & Reality Check
Apple has not announced a timeline for Docker Compose support. The docker/compose repo tracks this in issue #12934. Apple Container's own repo has #55 and #1846 as its most-requested features — both still open with no official response.
What this means for you: The Container Machines approach on this page is the working solution today. When compose arrives, your docker-compose.yml files will map directly — the shell scripts are a bridge, not permanent. The manual includes migration scripts designed to be replaced when compose lands, with comments marking what gets swapped out.
The Problem
Docker Compose doesn't exist for Apple Container. GitHub issues #55 and #186 are the most-requested features. The docker/compose repo has an open issue (#12934) requesting Apple Container support.
Apple hasn't committed to a timeline. But you can run multi-container setups today using Container Machines — the v1.0 feature that most tutorials don't even mention.
What Are Container Machines?
Container Machines is Apple Container's built-in mechanism for grouping multiple containers. Think of it as a lightweight alternative to docker-compose: you define a group of containers, their images, mounts, environment variables, and networking — then start them all with one command.
A Container Machine is defined via CLI flags (v1.0) — no YAML file yet, but the structure maps cleanly from docker-compose. Here's the mapping:
| docker-compose.yml | Container Machine |
|---|---|
services: | Containers within the machine group |
image: | --image flag per container |
ports: | Not needed — each container has its own IP (no port mapping) |
volumes: | --mount type=bind/volume,... flag per container |
environment: | --env KEY=val flags per container |
depends_on: | Shell script controls startup order |
networks: | All containers in a machine share the DNS domain (dev.internal) |
Real Example: Nginx + Node.js API
Here's a typical web stack. We'll convert it from docker-compose to Apple Container.
Original docker-compose.yml
version: '3'
services:
api:
image: myapp-api:latest
environment:
- PORT=3000
- NODE_ENV=production
volumes:
- ./src:/app/src
ports:
- "3000:3000"
nginx:
image: nginx:latest
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
ports:
- "80:80"
depends_on:
- api
Step 1: Start Both Containers in a Machine Group
# Create and start the machine group
container machine create web-stack
# Start the API container
container run --name api --machine web-stack \
--env PORT=3000 --env NODE_ENV=production \
--mount type=bind,src=$(pwd)/src,dst=/app/src \
myapp-api:latest
# Start the Nginx container
container run --name nginx --machine web-stack \
--mount type=bind,src=$(pwd)/nginx.conf,dst=/etc/nginx/nginx.conf \
nginx:latest
Step 2: Configure nginx.conf for Inter-Container Routing
# nginx.conf — proxy to the API container via DNS
server {
listen 80;
server_name _;
location / {
proxy_pass http://api.dev.internal:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
web-stack machine group and share the dev.internal DNS domain. Nginx reaches the API container at api.dev.internal:3000 — no port mapping needed. Each container has its own IP on the virtual network.
Step 3: Automate With a Shell Script
sleep 3 wait in the script below is a placeholder. In production, you need proper health checks. The manual covers 3 patterns:
1. HTTP health check:
until curl -s http://api.dev.internal:3000/health; do sleep 1; done
2. TCP port check:
until nc -z api.dev.internal 3000; do sleep 1; done
3. Container state polling:
until container ps --filter name=api --filter status=running | grep -q api; do sleep 1; done
The manual also covers container failure recovery — auto-restart, crash alerts, and startup ordering for 5+ service stacks. See Chapter 6 →
Wrap the commands in a script that handles startup order — this is your compose replacement:
#!/bin/bash
# start-stack.sh — Launch the web-stack machine group
set -e
echo "==> Creating machine group: web-stack"
container machine create web-stack 2>/dev/null || true
echo "==> Starting API container"
container run --name api --machine web-stack \
--env PORT=3000 --env NODE_ENV=production \
--mount type=bind,src=$(pwd)/src,dst=/app/src \
myapp-api:latest
echo "==> Waiting for API to be ready"
sleep 3
echo "==> Starting Nginx container"
container run --name nginx --machine web-stack \
--mount type=bind,src=$(pwd)/nginx.conf,dst=/etc/nginx/nginx.conf \
nginx:latest
echo "==> Stack running. Check status:"
container machine status web-stack
echo "API: http://api.dev.internal:3000"
echo "Nginx: http://nginx.dev.internal"
Make it executable and run:
chmod +x start-stack.sh
./start-stack.sh
Step 4: Verify Everything Works
# Check all containers in the machine group
container machine status web-stack
# Output should show:
# MACHINE CONTAINER STATUS IP
# web-stack api running 10.0.0.2
# web-stack nginx running 10.0.0.3
# Test the API directly
curl http://api.dev.internal:3000
# Test via Nginx
curl http://nginx.dev.internal
If both return expected responses, you've just replaced docker-compose with a working multi-container Apple Container setup.
Caveats & Limitations
No healthcheck built-in
docker-compose has healthcheck:. With Container Machines, add a polling loop in your start script (e.g., until curl -s http://api.dev.internal:3000/health; do sleep 1; done).
Startup order is manual
depends_on doesn't exist. Your shell script controls order — sleep durations or polling loops are your friend. The full manual includes robust health-check wrappers.
No YAML config
v1.0 is CLI-only. No declarative config file. Keep your startup script in version control — it is your config. A YAML parser may come in a future Apple Container release.
Free Docker → Apple Container Cheat Sheet
One-page PDF with every common Docker command mapped to its Apple Container equivalent. Plus we'll ping you when Compose support ships — no spam, just that one email.
No spam. Just the cheat sheet + one notification when Compose launches.
Want the Full Migration Manual?
The compose alternative above works, but it's the tip of the iceberg. The manual covers production hardening, 5 stack templates, and the conversion script pack.
Get the Complete Manual — $19Last updated: July 1, 2026