Docker CLI compatibility shim over Apple's container CLI.
Project description
docker-for-apple-container
Run
dockeranddocker composeon macOS, backed by Apple's nativecontainerCLI. No Docker Desktop.Website: https://appautomaton.github.io/docker-for-apple-container/ · PyPI: https://pypi.org/project/docker-for-apple-container/
docker-for-apple-container is a small docker command wrapper for Apple's
container CLI. It lets tools that expect a docker binary run against Apple
container on macOS, without installing Docker Desktop, Podman, or a
third-party adapter.
It is a stateless translator, not a Docker replacement. It maps each Docker
command to a clean Apple container equivalent and fails loudly on the rest.
Apple container is the single source of truth, so the shim persists nothing of
its own (no sidecar file, registry, or database). Even docker compose stays
stateless: project membership is stored as labels in Apple's own object
store, exactly as Docker Compose does, so every verb reconstructs the project
by querying Apple rather than reading shim-owned state.
Install
Every method below gives you a docker command backed by Apple container.
Use them on a Mac that runs Apple container rather than Docker Desktop.
With Homebrew:
brew install appautomaton/tap/docker-for-apple-container
With pip:
pip install docker-for-apple-container
From source, symlink the launcher onto your PATH:
git clone https://github.com/appautomaton/docker-for-apple-container.git
cd docker-for-apple-container
ln -sf "$(pwd)/bin/docker" ~/.local/bin/docker
After any of these, run docker as usual. If a tool resolves its Docker binary
from an environment variable or config setting, point that at the installed
docker (or the repo's bin/docker).
Requirements
- macOS with Apple
container1.0.0 - Python 3.9+ (standard library only, no third-party packages)
container system statusshould report the apiserver as running
Start it with:
container system start
Supported Docker Subset
Three tiers. Anything outside them fails with an explicit exit-64 error instead of pretending to work.
Fully translated (the core contract)
docker versiondocker info --format "{{.Driver}}"docker build -f DOCKERFILE -t TAG CONTEXTdocker image inspect IMAGE --format "{{json .Config.Entrypoint}}"docker run -d ... IMAGE CMD...docker create ... IMAGE CMD...uses the same flag translation asrunand prints the new container IDdocker ps -a --filter ... --format ...docker inspect --format "{{.State.FinishedAt}}" CONTAINERdocker start CONTAINERdocker exec [-i] [-e KEY=VALUE] CONTAINER CMD...docker stop -t N CONTAINERdocker rm [-f] CONTAINER
Translated extras
docker logs [-f] [--tail N] CONTAINER.--tail Nmaps to Apple-n N(and--tail allto "print all").--sinceand--timestampshave no Apple equivalent, so they are refused.docker stats [--no-stream] CONTAINER. Go-template--formatis refused. Apple--formataccepts onlyjson|table|yaml|toml.docker cp SRC DEST. The positionalcontainer:pathform maps 1:1 onto Applecontainer copy. Docker-aand-Lflags are refused.docker restart [-t N] CONTAINER..., composed fromstop+start(Apple has norestart). No state is kept between the two calls.docker export [-o FILE] CONTAINERmaps ontocontainer export -o. Note: Applecontainer exportrequires the container to be stopped (Docker also exports running ones). The shim surfaces Apple's "container is not stopped" error rather than silently stopping it for you.docker login [-u USER] [--password-stdin] SERVERanddocker logout SERVERdelegate tocontainer registry login/logout. Apple stores the credential. The shim keeps nothing. Docker-p/--passwordis refused in favor of--password-stdin.docker system infomaps todocker info.docker system prune [--volumes]runs Apple'sprune+image prune+network prune(+volume prune). It is non-interactive: there is no confirmation prompt, and-f/-aare no-ops.
Thin passthrough (basic forms only)
docker images, docker pull, docker push, docker tag, docker save,
docker load, docker rmi (top-level aliases for docker image <sub>),
docker image <sub> (pull/rm/tag/push/save/load/prune/ls),
docker network <sub> and docker volume <sub>
(create/ls/rm/inspect/prune), and docker kill [-s SIG] forward to the
matching Apple container command.
Subcommand names and common flags line up, but Docker-only flags are not
translated. Go-template --format on ls-style commands is refused rather
than mis-forwarded, and subcommands Apple lacks (e.g. network connect) fail
loudly.
Compose (stateless orchestration)
docker compose up/down/ps/logs/build/config/ls orchestrate multi-service
stacks without persisting any shim-owned state. Apple container has no
native compose, so the shim parses the compose file and issues a sequence of
container commands, but it keeps no project file. Instead every resource is
tagged with Docker's own label schema (com.docker.compose.project,
com.docker.compose.service, and so on) on the containers, the project network,
and any named volumes. down/ps/logs/ls reconstruct the project purely by
querying Apple and filtering on those labels. Only up/build/config need to
read the compose file.
- Project name resolves like Docker:
-p NAME→COMPOSE_PROJECT_NAME→ the file'sname:→ the directory basename. - Service discovery. Apple does not resolve service names by DNS without an
admin
container system dnsdomain. Instead, after services start, the shim appends<ip> <service>lines to each container's own/etc/hostsfile (IPs read live fromcontainer inspect). That file lives in the container's ephemeral layer and is discarded when the container is removed. The macOS host's/etc/hostsis never touched. host.docker.internal. The same/etc/hostsinjection also publisheshost.docker.internalandgateway.docker.internalpointing at the container's gateway, which on Applecontaineris the macOS host. This mirrors Docker Desktop (which adds these names automatically on macOS/Windows), so a service that dials the host by that name (for examplehttp://host.docker.internal:8317) works unchanged. The gateway is read per-network fromcontainer inspect, not hardcoded. Injection is idempotent and skipped with a warning on shell-less images (e.g. distroless). It is compose-only. Baredocker runis left alone, since injecting into a possibly short-lived container would race its exit (Apple has no--add-hostflag to set it at creation, so it must be done via a post-startexec).- Named volumes map onto Apple-native volumes (
container volume create), scoped as<project>_<volume>. Host-path mounts become bind mounts, with relative paths resolved against the compose file's directory. - Teardown is self-coherent.
downremoves the project's containers (found by label), then removes the network (and with-v, the volumes) only if the shim created them (verified via the project label), never external ones. upis idempotent: it removes the project's previous containers before recreating, so re-running never accumulates duplicates.- YAML is parsed by a small dependency-free subset parser (block maps and
sequences, flow collections, quoted scalars, comments, and
${VAR:-default}interpolation). Anchors, multi-document streams, and|/>block scalars are out of scope.
Compose keys with no Apple equivalent (restart, healthcheck, privileged,
hostname, secrets, configs, deploy replicas, and the like) are parsed but ignored,
with a one-line warning per key so behavior is never silently misrepresented.
Refused, by design
Commands and flags with no verified Apple equivalent fail loudly:
docker system events (a stateful watcher), docker commit/diff/rename/
history/import (no Apple equivalent), docker run --network=none,
docker run --add-host/--hostname, and any unknown command.
Caveats
--security-opt,--pids-limit, and--storage-optonrunare accepted as silent no-ops. Applecontainerdocuments no equivalent, so a container may be less constrained than the flag implies.docker run -v host:ctr:robecomes an Apple--mountbind (onlyromode is honored).--tmpfsoption suffixes are reduced to the mount path.
State
The shim is stateless. It does not persist Docker-shaped metadata, cache files,
or a support directory. Apple container is the source of truth, so direct
Apple container changes are reflected on the next shim command. Compose is no
exception: project bookkeeping lives in Apple's label store, not in any
shim-owned file. See the Compose section above.
Tests
Unit tests use a fake container binary and do not start real containers. They
cover the core Docker command contract in tests/test_hermes_contract.py, and
compose in tests/test_compose.py (parser, interpolation, topo sort,
translation, and label-based orchestration):
python3 -m unittest discover -s tests -v
Live smoke testing against Apple container is intentionally manual because it
starts and removes containers. The compose path has been verified end-to-end
against Apple container 1.0.0 (multi-service up, label reconstruction,
service-name resolution, build:, named volumes, and clean down/down -v).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file docker_for_apple_container-0.1.2.tar.gz.
File metadata
- Download URL: docker_for_apple_container-0.1.2.tar.gz
- Upload date:
- Size: 110.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
12882321661a585e05c79a31eb5d920659b363facdc516b010e9902845971184
|
|
| MD5 |
3b7c1ffff8918fd955b38e713f6a0e9c
|
|
| BLAKE2b-256 |
b56e386743ee7a6539de50b17114f51fb70edd8657c1c9fcf3db797e3ba113c5
|
Provenance
The following attestation bundles were made for docker_for_apple_container-0.1.2.tar.gz:
Publisher:
release.yml on appautomaton/docker-for-apple-container
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
docker_for_apple_container-0.1.2.tar.gz -
Subject digest:
12882321661a585e05c79a31eb5d920659b363facdc516b010e9902845971184 - Sigstore transparency entry: 1990054962
- Sigstore integration time:
-
Permalink:
appautomaton/docker-for-apple-container@4eca31d3cc714f2a7148d5c102cded1d87c7e202 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/appautomaton
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4eca31d3cc714f2a7148d5c102cded1d87c7e202 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file docker_for_apple_container-0.1.2-py3-none-any.whl.
File metadata
- Download URL: docker_for_apple_container-0.1.2-py3-none-any.whl
- Upload date:
- Size: 29.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8ebf42ce3c87dc3273d02e66d6b8f41ffba249beaa5fbc27145205fcfd90ce1f
|
|
| MD5 |
ed59fc407dfc01231ff776ebdd0f32ec
|
|
| BLAKE2b-256 |
c45f6db034086a00543c63cdb6a0d5fdca95df2511aed2552119048edd29656d
|
Provenance
The following attestation bundles were made for docker_for_apple_container-0.1.2-py3-none-any.whl:
Publisher:
release.yml on appautomaton/docker-for-apple-container
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
docker_for_apple_container-0.1.2-py3-none-any.whl -
Subject digest:
8ebf42ce3c87dc3273d02e66d6b8f41ffba249beaa5fbc27145205fcfd90ce1f - Sigstore transparency entry: 1990055047
- Sigstore integration time:
-
Permalink:
appautomaton/docker-for-apple-container@4eca31d3cc714f2a7148d5c102cded1d87c7e202 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/appautomaton
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4eca31d3cc714f2a7148d5c102cded1d87c7e202 -
Trigger Event:
workflow_dispatch
-
Statement type: