Teaching Claude Apple's Native "Container" CLI
It's so easy
Earlier today I finally upgraded to Mac OS 26. That means native container time.
Within 30 minutes I had a comparable start script to our existing docker compose based config.
I asked claude to run the “container” cli tool to figure out how it works, it is apple’s native alternative to docker.
That’s mostly all it took, it ran the help commands for every arg listed and then knew what to do. Apple’s framework is fully compatible with OCI docker images that exist on docker hub already.
I asked it to make a small bash script to setup the containers and start them.
The apple setup is more bare-bones. There’s no compose style yaml config. There is no automatic container-name-to-dns.
That means you have to manually run some commands to make a network so your postgres db can talk to your app server.
With a little trial and error and telling Opus this, it succeeded.
container network create "$NETWORK" 2>/dev/null || trueMaking a network is that simple, then when running a container, pass the network flag
# Postgres
echo "==> Starting postgres..."
container run -d \
--name "$PG" \
--network "$NETWORK" \The trickiest part however, is to figure out what the IP’s of all these are. Thankfully apple did a great job on the —help arg to all their commands. Claude figured out one approach fast:
# Resolve container IPs and generate env file
echo "==> Resolving container IPs..."
PG_IP=$(get_container_ip "$PG")
REDIS_IP=$(get_container_ip "$REDIS")
RABBITMQ_IP=$(get_container_ip "$RABBITMQ")
RESOLVED_ENV=$(make_env_file "$PG_IP" "$REDIS_IP" "$RABBITMQ_IP")
echo " Postgres: $PG_IP Redis: $REDIS_IP RabbitMQ: $RABBITMQ_IP"And if you’re wondering, the function called is this:
# Apple Container Framework doesn't have built-in DNS between containers,
# so we resolve IPs dynamically and template them into the env file.
get_container_ip() {
container inspect "$1" 2>/dev/null \
| python3 -c "import sys,json; print(json.load(sys.stdin)[0]['networks'][0]['ipv4Address'].split('/')[0])"
}json is returned from container inspect and then we can inject these into an env file in our app instance. We replace the template holders for the DB url and others with the resolved container IP and then start the app server at the end.
# Django dev server (source bind-mounted for live reload)
echo "==> Starting django..."
container run -d \
--name "$DJANGO" \
--network "$NETWORK" \
-u root \
-m 2G \
-p 8080:8080 \
-v "${ROOT}:/app" \
--env-file "$RESOLVED_ENV" \
"$DJANGO_IMAGE" \
python manage.py runserver 0.0.0.0:8080We can mount and live sync files as you would expect. So our django dev server can hot reload.
It’s crazy how fast I could do this, by simply telling opus what the cli tool is, and then asking it to hit a few endpoints with curl to validate what it did was working.
In my early testing this application is using about 1.5gb of ram, where it would use 2.5-3gb under docker. Docker also was a massive memory and cpu hog with its virtual machine, so I am hoping I can leave more containers running under this native virtualization framework apple has added. Time will tell.
Also, Claude’s desktop app uses this same framework to do certain things!

