Skip to content
COMA

Docker Compose

Run a Compose project on a remote machine with bind mounts, localhost ports and builds working as they do on your laptop.

COMA does not run Compose for you. You keep running docker compose on your computer; COMA points the Docker CLI at the machine, keeps your source on the machine so bind mounts resolve, and mirrors published ports to localhost.

This guide assumes you have added a machine with a working engine.

Connect the project

There are two ways to set this up. A workspace is the shorter one, and it is what coma.yaml is for.

Write a coma.yaml once. coma workspace init names the Compose file it finds in the directory.

coma workspace init

Bring the workspace up. This registers it, starts the endpoint for the machine, switches the Docker CLI's context to coma and starts syncing the source. It does not start containers.

coma workspace up --machine dev

Then start the stack, in this terminal or any other:

docker compose up

Running coma workspace up again is safe. See Your first workspace and the coma.yaml reference.

Bind mounts

A bind mount such as ./src:/app/src names a path on your computer. When the container is created through COMA, that path is replaced by the synced copy on the machine, so the container sees your code and your edits.

  • The source must be inside a synced directory. A bind mount outside every synced directory is refused, and Docker prints COMA's reason, which names sync_bind_source_unmanaged. Sync a directory that contains it (coma sync watch --path <dir>), or move the file into the project.
  • COMA waits for your edits. Before a container is created, COMA waits until your latest changes are on the machine. If they do not arrive in time the container is not created, and the reason names sync_barrier_timeout. Check coma sync status.
  • Ignored files are not copied. .gitignore, .comaignore and .git/ are not synced. Binding an ignored file, or an ignored directory that has content on your computer, is refused with sync_bind_source_missing and a hint to add the path to spec.sync.include in coma.yaml.

Machine-local data directories

Compose projects often bind a data directory that is gitignored and does not exist yet, for example ./data:/var/lib/postgresql/data. Docker creates a missing bind source, and so does COMA: when the source is missing on your computer, or is an empty ignored directory, COMA creates it on the machine and leaves its contents there.

That data stays on the machine. It is never copied back to your computer. coma workspace delete --remote removes the workspace's copy on the machine, including files a container wrote as root.

Ports

Every TCP port a container publishes on the machine is also open on your computer at the same number. With ports: ["3000:3000"] in the Compose file, the app is at localhost:3000. On the machine, COMA publishes the port on 127.0.0.1 only. See Ports.

coma port list

If something on your computer already listens on that port, the mirror cannot open: coma port list shows the state local port in use and names the process that holds it. A local Postgres, Redis or a local Podman machine are the usual owners. Stop the local service, or declare the port in coma.yaml at another local number. A declared port replaces the mirror of the same remote port while the workspace is up:

spec:
  ports:
    - name: db
      remote: 5432
      local: 15432

Builds

docker compose up --build and docker compose build run on the machine's engine. The build context is uploaded from your computer, so a large context costs time on every build: keep .dockerignore current. During dogfooding, a 31.6 MB context took 34 s to upload, most of it a cache directory missing from .dockerignore.

Images and build cache stay on the machine and add up quickly. Run docker system prune from time to time; it runs on the machine too.

On a Podman engine, Compose builds work differently and are slower. See Podman.

Use Compose v2

Use docker compose (Compose v2). Compose v1, the old docker-compose 1.x program, cannot read compose.yaml.

The trap is PATH order. podman compose, and a docker compose alias for it, run whichever docker-compose comes first on PATH. If you have two installed, a fresh shell (such as the one a coding agent starts) can pick v1 while your terminal picks v2. coma doctor lists every Compose v1 on PATH:

WARN  docker-compose         Compose v1 cannot read compose.yaml: /opt/homebrew/bin/docker-compose (1.25.5), later on PATH; shells that order PATH differently run it
                             -> remove the old docker-compose (podman compose and a `docker compose` alias for it run whichever comes first on PATH)

Remove the old docker-compose.

Images that behave differently on the machine

A stack that works locally can fail on the machine for reasons unrelated to COMA:

  • A different kernel. The machine runs its own kernel, not your laptop VM's. For example, recent MongoDB images refuse kernels 6.19 and later, which Ubuntu 26.04 ships. Pin an image that supports it (such as mongo:7.0) or use a machine image with an older kernel, such as Ubuntu 24.04 LTS.
  • Floating tags. The machine pulls :latest (or any moving tag) fresh, while your laptop may hold an older copy. If an image no longer matches your checkout's configuration, pin the tag to the release the checkout expects.
  • Unqualified image names on Podman. See Podman.

Secrets in .env files

coma workspace plan and up warn when a .env file in the source would be synced to the machine, because such files often hold secrets. If the machine does not need the file, add it to .gitignore or .comaignore.

Stop

ToRun
Stop the containersdocker compose down
Stop syncing the workspacecoma workspace down
Switch the Docker CLI backcoma disconnect
Remove the copy on the machinecoma workspace delete --remote

coma workspace down leaves containers, the endpoint and the copy on the machine alone.

On this page