Your first workspace
Describe a project in coma.yaml, then plan, bring up, check and stop it with coma workspace.
A workspace is a project directory with a coma.yaml that names its machine, engine, sync rules and ports. coma workspace up connects to the machine and keeps the source synced there, so docker compose up in any terminal runs remotely with bind mounts and localhost ports working. See Workspaces for the model.
You need a machine with an engine first: Add a machine, then Bootstrap a bare VM if it has none. The examples use a machine called dev and a project in /Users/you/src/web with a compose.yaml.
Write coma.yaml
In the project directory:
coma workspace initWrote /Users/you/src/web/coma.yaml:
apiVersion: coma.sh/v1alpha1
kind: Workspace
metadata:
name: web
spec:
source:
path: .
runtime:
compose:
files:
- compose.yaml
sync:
mode: one-way-safe
Next: coma workspace plan, then coma workspace upinit names the directory as the workspace and lists the Compose file it finds. It writes a machine only when you pass --machine: machine names are per user, and coma.yaml is meant to be committed. It writes an engine only with --engine docker or --engine podman. --name sets another workspace name, and --force replaces an existing coma.yaml.
The smallest valid coma.yaml is the header and a name:
apiVersion: coma.sh/v1alpha1
kind: Workspace
metadata:
name: webThe full schema is in the coma.yaml reference.
COMA finds coma.yaml (or coma.yml) in the current directory or its parents, stopping at the repository root. --file points at another one.
Check it
coma workspace validate/Users/you/src/web/coma.yaml is valid (web)validate reports every problem at once, each with its line, column and YAML path:
Error: /Users/you/src/web/coma.yaml is not a valid workspace manifest:
/Users/you/src/web/coma.yaml:17:15 spec.ports.0.remote: required: a port number from 1 to 65535 (workspace_manifest_invalid)Sections that the schema reserves but COMA does not implement yet are accepted and listed as not implemented yet, ignored, never silently dropped.
Which machine and engine
COMA picks the machine and engine in this order. The first one set wins:
| Order | Machine | Engine |
|---|---|---|
| 1 | --machine | --engine |
| 2 | spec.target.machine in coma.yaml | spec.runtime.engine in coma.yaml |
| 3 | the current context's machine (coma use) | the machine's only working engine, or the context's |
With none of the three for the machine, COMA stops with workspace_target_required. If the machine has both Docker and Podman and nothing picks one, COMA stops with engine_selection_required and asks for --engine or spec.runtime.engine. See Contexts and Engines.
See the plan
plan shows what up would do, and where each choice came from. It changes nothing.
coma workspace plan --machine devWorkspace web (/Users/you/src/web/coma.yaml)
machine: dev (from --machine)
engine: docker (from the machine (its only working engine, or the context's))
source: /Users/you/src/web
ACTION SUBJECT DETAIL
create workspace register web from /Users/you/src/web/coma.yaml
start endpoint dev-docker endpoint; the Docker context `coma` points at it
sync source /Users/you/src/web to dev, kept current (one-way-safe: what changes on the machine is never overwritten)
noop runtime not started by COMA yet: run `docker compose up` once the workspace is up (compose files: [compose.yaml])plan reads the machine's inventory from COMA's local state. If the inventory is out of date, refresh it with coma machine discover dev.
Bring it up
coma workspace up --machine devWorkspace web is up on dev (docker 28.2.2 over ssh-streamlocal)
docker (and Compose) in any terminal now run on the machine
Next: docker compose up. `coma workspace status` shows health; `coma disconnect` switches the CLIs back.up registers the workspace, starts the endpoint for the machine's engine, makes the Docker context coma current (and, for Podman, podman's default connection), and syncs the source to the machine. It also prints how many files it synced and where. Running up again is safe.
up does not start containers. That is the next command, in this terminal or any other:
docker compose upBind mounts of project files now see the machine's copy of your source, and published ports open on your localhost.
--no-connectkeeps the Docker CLI's and podman's defaults; usedocker --context comainstead.--no-syncskips syncing; bind mounts of the source are then refused.
Files in .gitignore, .comaignore and .git/ are not copied. plan warns about .env files that would be synced, because they often hold secrets. See Source sync.
Declare ports
Every TCP port a container publishes is mirrored on your localhost at the same number, with or without coma.yaml. Declare a port in spec.ports to give it a name, map it to another local number, or forward a port on the machine:
spec:
ports:
- name: web
remote: 3000local defaults to the remote number; it can be another port, auto or disabled. Only TCP is supported. plan lists one forward action per declared port, and up reports where each one is reachable. See Ports.
Check its health
coma workspace statusWorkspace web (ws_01K6PZ4Q8J3V7N2XG5T9RMB0CD): up on dev, engine docker
last up 2026-10-04 14:20:11
PASS ManifestValid
PASS CompatibilityEndpointReady dev-docker: ready
PASS SyncReady watching, 214 files
PASS PortsReady coma.yaml declares no ports; published ports are mirroredEach condition is PASS, WARN or FAIL with a message. status also reports drift when coma.yaml changed after the last up. coma workspace list shows every registered workspace.
Stop it
coma workspace downWorkspace web is down: sync stopped; the copy on the machine staysdown stops syncing and stops forwarding the ports declared in coma.yaml. It leaves the containers, the copy on the machine and the endpoint alone. To finish:
docker compose downcoma disconnectDelete it
coma workspace deleteDeleting workspace web will:
- stop syncing /Users/you/src/web and forget its sync state
- stop forwarding its coma.yaml ports
- forget workspace web
It keeps:
- the source directory /Users/you/src/web
- containers, images and volumes on the machine
- the endpoint and Docker context (`coma disconnect` switches back)delete shows what it will do and asks first; pass --yes in scripts. It never touches your source directory, containers, images, volumes or the endpoint. The copy of the source on the machine, under ~/.coma/workspaces, stays unless you add --remote:
coma workspace delete --remoteNext
- Docker Compose on a COMA machine.
- Coding agents in a workspace.
- coma.yaml reference.