---
name: coma
description: Run and debug containers on a remote machine through COMA (coma.sh). Use when the project has a coma.yaml, `docker context show` prints coma, the user mentions COMA or the coma CLI, or docker, Compose, bind mounts or localhost ports behave as if the engine is somewhere else. Covers workspaces, sync, mirrored ports, `--json` output and error codes, and catching commands that ran on the laptop instead of the machine. Not for installing COMA or choosing a cloud provider.
license: Apache-2.0
compatibility: Needs the coma CLI on macOS or Linux, and the docker or podman CLI.
metadata:
  coma-version: v0.1.0-rc.4-4-gae85032
  docs: https://coma.sh/docs
---

# COMA

COMA points the user's Docker and Podman CLIs at a Linux machine they own, over SSH. After `coma connect` (or `coma workspace up`), plain `docker`, `docker compose`, test suites and `curl localhost:<port>` work as usual, but the containers and the memory they use are on the machine.

Your job is to keep it that way: use the machine, check COMA's state when something looks wrong, and report problems instead of quietly falling back to a local engine.

## Rules

1. **Never work around COMA.** Do not set `DOCKER_HOST`, `DOCKER_CONTEXT` or `DOCKER_CONFIG`, switch Docker contexts, start Docker Desktop, Colima or a Podman machine, or use another socket to make a failing command pass. Each of these sends containers to the laptop and makes the task look done when it is not. Diagnose (below) and tell the user.
2. **Add `--json` to every `coma` command you run.** The result is one JSON document on stdout. On failure stdout is empty and stderr holds an error envelope. Branch on `error.code` and the exit code; never parse `error.message`. Run `error.details.hint` when it is a read-only command, and show it to the user otherwise.
3. **Ask the user before anything that trusts a host or changes a machine or files.** COMA never prompts in your shell (there is no terminal); instead it fails and names the flag that confirms. Do not add that flag yourself. Ask first for:
   - `--host-key SHA256:…` on `coma machine add`, `bootstrap` or `discover`. The user must check the fingerprint against the server's own key; a mismatch can be a man-in-the-middle.
   - `--yes` on `coma machine bootstrap` (installs packages, may restart Docker), `coma workspace up` in `one-way-mirror` mode (overwrites and deletes files on the machine) and `coma workspace delete`.
   - `--replace`, `--remote`, `--detach`, `coma sync resolve`, `coma machine remove`, `coma daemon stop`, and enabling `bidirectional` sync.
4. **Prefer plain `docker` after connecting over `coma docker`.** `coma docker …` and `coma podman …` run the CLI on the machine itself: published ports open on all its interfaces, nothing is mirrored to localhost, and bind-mount paths are paths on the machine. Use them only when the user asks or to inspect the machine directly.

## Before running containers

Check where `docker` points, then the workspace if there is one:

```bash
docker context show            # "coma" means connected through COMA
coma workspace status --json   # when there is a coma.yaml here or above
```

`WorkspaceStatus` has conditions that are each `true`, `false` or `unknown`: `ManifestValid`, `CompatibilityEndpointReady`, `SyncReady` and `PortsReady`. The command exits 0 even when a condition is false, so read them. A `coma.yaml` changed since the last `up` appears in `warnings`.

| Situation | Do |
| --- | --- |
| `coma.yaml` exists, workspace not up (`workspace_not_found`) or a condition is false | `coma workspace plan --json` to see what would happen, then `coma workspace up --json` |
| No `coma.yaml`, context is not `coma` | Ask the user which machine (`coma machine list --json`), then `coma connect <machine> --json` |
| Context is `coma` and conditions are true | Go ahead with `docker` / `docker compose` |

`coma workspace up` connects the Docker CLI, starts the endpoint and syncs the source. It does not start containers: run `docker compose up -d` next. Running `up` again is safe.

## While containers run

- **Bind mounts need sync.** A mount like `./src:/app/src` works only inside a directory COMA syncs to the machine (the workspace source, or `coma sync watch --path <dir> --json`). Docker shows COMA's refusals with a code in parentheses: `sync_bind_source_unmanaged` (outside every synced directory), `sync_bind_source_missing` (ignored by sync; `coma sync explain <path> --json` says which rule), `sync_barrier_timeout` (latest edits had not arrived; check `coma sync status --json`, then retry).
- **Ports.** Every TCP port a container publishes is mirrored to the same number on `localhost`, so `curl localhost:3000` reaches it. On the machine COMA publishes on `127.0.0.1` only; it never opens public ingress, and a `coma.yaml` port with `visibility: public` is refused. `coma port list --json` shows each port and its state; `local port in use` means a program on the laptop holds that number.
- **Tools that ignore Docker contexts** (Docker SDKs, Testcontainers, anything reading `DOCKER_HOST`) need COMA's stable socket. Your shell commands may each start a fresh shell, so set it in the same command: `eval "$(coma endpoint env)" && npm test`. This is the one sanctioned way to set `DOCKER_HOST`; it points at COMA, not around it.
- **Interrupts.** Ctrl-C or a timeout is forwarded to the machine, but a container whose main process ignores it keeps running. Add `--init` to `docker run` commands you may interrupt, and check `docker ps` afterwards.
- **Sync conflicts.** In the default `one-way-safe` mode a file changed both locally and on the machine is a conflict and the machine's copy is kept. `coma sync status --json` lists conflicts. Ask the user which copy wins before `coma sync resolve <path> --keep local|machine`.

## When something is wrong

Signs: `docker` errors mention a local socket, containers or images the user expects are missing, `localhost` reaches the wrong service, or Compose cannot read `compose.yaml`.

```bash
coma doctor --json
```

`DoctorReport` has `checks` (each with `id`, `status` of `PASS`/`WARN`/`FAIL`, `message`, and `remediation` when there is one) and a `summary`. It exits 10 (`doctor_failed`) when any check fails, and writes the report to stdout either way. Look first at:

| Check | Means |
| --- | --- |
| `local-fallback` | Containers were created on a local engine while connected. Something bypassed COMA. |
| `docker-credentials` | Docker's `config.json` names a credential helper that is not installed, so every pull fails. Do not "fix" it with a temporary `DOCKER_CONFIG`; that drops the `coma` context. Tell the user to remove `credsStore`/`credHelpers` or install the helper. |
| `docker-compose` | A Compose v1 is first on PATH and cannot read `compose.yaml`. Use `docker compose` (v2). |
| `cli-routing` | `docker` is an alias for podman while connected to a Docker engine, or podman does not follow the machine. The user should connect with `--engine podman`. |

For one endpoint: `coma endpoint list --json`, then `coma endpoint doctor <machine>-<engine> --json`. To see what COMA changed in the last container request (ports moved to loopback, bind sources mapped, or why it was refused): `coma endpoint doctor <machine>-<engine> --explain-last --json`.

Report what the checks say and the remediation, then stop. More detail: [references/troubleshooting.md](references/troubleshooting.md).

## After finishing a task

Run `coma doctor --json` and read `local-fallback`. If it reports containers created locally during your work, say so plainly: the work did not run where the user expected.

## Exit codes

| Exit | Meaning |
| --- | --- |
| 0 | Success |
| 1 | Internal error (a COMA bug, or no more specific code) |
| 2 | Invalid usage, or confirmation needed (`confirmation_required`, `workspace_manifest_invalid`, `*_target_required`) |
| 3 | Not found (`machine_not_found`, `workspace_not_found`, …) |
| 4 | Conflict (`local_port_in_use`, `docker_context_conflict`, `workspace_plan_blocked`, …) |
| 5 | Auth or trust (`ssh_host_key_unknown`, `ssh_host_key_mismatch`, `machine_auth_failed`, …) |
| 6 | Unavailable (`machine_unreachable`, `daemon_unavailable`, `engine_unavailable`, …); check `error.retryable` |
| 7 | Timeout |
| 8 | Unsupported |
| 10 | Local state, config, or a failed doctor check |
| 130 | Interrupted |

`coma docker` and `coma podman` exit with the remote command's own status, so an exit of 7 there is not a COMA timeout. Every error code and what to do about it: [references/errors.md](references/errors.md).

## Reference

- [references/errors.md](references/errors.md): every error code, its exit code and the action to take.
- [references/coma-yaml.md](references/coma-yaml.md): writing or fixing a `coma.yaml`.
- [references/troubleshooting.md](references/troubleshooting.md): common problems and their fixes.
- Full docs: https://coma.sh/docs (CLI reference at https://coma.sh/docs/reference/cli). `coma <command> --help` is always current for the installed version.
