Run a Compose stack
A web service and a database, brought up together from the Stacks view — on any engine.
Hopper reads the compose file and runs the services itself, so this works on Apple Containers with no Docker and no Compose binary anywhere on the machine.
1. A stack to run
# compose.yaml
services:
db:
image: postgres:17-alpine
environment:
POSTGRES_PASSWORD: example
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- pgdata:/var/lib/postgresql/data
web:
image: nginx:alpine
ports:
- "8080:80"
depends_on:
- db
volumes:
pgdata:
The PGDATA line is not decoration. A named volume can arrive with a
lost+found in it, and Postgres refuses to initialise into a non-empty
directory. Pointing at a subdirectory sidesteps it, and costs nothing when the
volume is empty.
2. Bring it up
Open Stacks, then Open compose file…, and pick the file or the directory it is in.
Hopper reads it, creates the network and the pgdata volume, pulls both images,
and starts db before web because depends_on says so. Every line of that
appears in the panel as it happens.
3. Watch it in Hopper
The project appears in the list with both services under it.
The grouping is read from the com.docker.compose.* labels on the containers,
not from the YAML — which means a stack someone else started with
docker compose up, or one started before Hopper was open, shows up just the
same. It works the other way too: docker compose ls on a machine that has it
will list the project Hopper just created.
Click into Containers and select db. The Logs tab shows Postgres
initialising. If you got PGDATA wrong, this is where you will see it say so.
4. Drive it
Start and stop the whole project from the Stacks view, or individual services from Containers. Both are the same containers; there is no separate state.
5. Tear it down
Down on the project row stops and removes both containers and the network. The volume outlives it — which is the point of a volume, and the reason the database survives a rebuild.
Press Up again and the stack comes back with its data. Press Up on a stack that is already running and nothing is disturbed: each container carries a hash of the service it came from, and one that still matches is left alone.