Ports
Published container ports are mirrored to localhost at the same number, and published only on 127.0.0.1 on the machine.
Every TCP port a container publishes on the machine is also available on your localhost at the same number. On the machine, COMA publishes on 127.0.0.1 only.
localhost on your computer
While you are connected (coma connect or coma workspace up), COMA watches the engine for containers that start and stop. For each published TCP port it listens on 127.0.0.1 and ::1 on your computer at the same port number, and forwards each connection over SSH to the port on the machine.
docker run -d -p 8080:80 nginx
curl localhost:8080- The local number always equals the machine's host port. Tools that ask the engine which port a container got, such as Testcontainers,
docker portor-Pwith random ports, find the right number with no configuration. docker runanddocker startreturn only after the mirrors are listening (COMA waits at most 2 seconds), so a client can connect immediately.- Mirrors close when the container stops.
- Only TCP is mirrored. A UDP publication shows as
port_protocol_unsupported.
127.0.0.1 on the machine
With no host address, Docker publishes ports on every interface of the machine, and on a rented VM that puts your development database on the internet. Firewalls such as ufw do not stop it, because Docker's rules run first.
For containers created through COMA's endpoint, COMA rewrites the publication before the engine sees it:
- no address,
0.0.0.0or::becomes127.0.0.1; -P(publish all) becomes explicit127.0.0.1bindings with random host ports;- an explicit address you chose, such as
-p 203.0.113.10:8080:80, is kept and reported.
docker inspect therefore shows 127.0.0.1 as the host IP. The port is reachable from your computer through COMA, and not from the network. COMA never creates public ingress.
Containers started without COMA's endpoint are not rewritten: over a separate SSH session, or with coma docker and coma podman, which run the engine's CLI on the machine. COMA still mirrors their ports while you are connected; coma port list marks those published on every interface as all-interfaces and warns. To make Docker itself default to 127.0.0.1 for every container, bootstrap with --harden-publish:
coma machine bootstrap dev --engine docker --harden-publishThat step sets "ip": "127.0.0.1" in Docker's daemon.json, keeps a backup of the file and restarts Docker. The plan shows it before anything changes.
coma port list
coma port listcoma port list shows each mirrored or declared port with these columns:
| Column | Meaning |
|---|---|
LOCAL | The address on your computer |
SOURCE | mirror, or <workspace>/<name> for a port declared in coma.yaml |
CONTAINER | The Compose service or container name |
REMOTE | The machine and its host port, with the container port |
REMOTE EXPOSURE | loopback, explicit or all-interfaces |
STATE | ready, pending, disabled, or why it is not ready |
With nothing mirrored, it says so:
No ports mirrored. Publish one, e.g. `docker run -d -p 8080:80 nginx`, while connected (`coma connect`).Another local port: spec.ports
Mirroring keeps the machine's number. To use a different local number, declare the port in the workspace's coma.yaml. A declared port replaces the mirror of the same remote port while the workspace is up.
spec:
ports:
- name: db
remote: 5432
local: 15432
- service: web
remote: 80
local: auto
- remote: 6379
local: disabledremoteis the machine's host port. Withservice, it is the container port of that Compose service, wherever the engine publishes it.localis a port number,autoordisabled, and defaults to the remote number.autopicks a free port and keeps it acrossworkspace upruns while it stays free.disabledforwards nothing and also turns off the mirror for that port.- A declared service port whose container is not running is
pending: COMA holds the local port and refuses connections until the service starts. If the service is recreated on another host port, COMA follows it and keeps the local port. - Only TCP is supported, and
visibility: publicis rejected.
Declared ports are for you and for tools that read COMA's output (coma port list --json, coma workspace status --json). Tools that ask the engine for a port number still expect the mirrored one. See the coma.yaml reference for every field.
When the local port is taken
COMA never moves a mirror to another local number on its own, because tools that ask the engine for the port could not find it. If something on your computer already uses the port, the mirror's state is local_port_in_use (local port in use in the table), and coma port list names the program that holds it when lsof can tell:
port 5432: already used on this computer by postgres (pid 4242)Common causes are a local database, or another machine's container publishing the same port while you are connected to both. To resolve it, do one of these:
- Stop the local program, then restart the container (
docker restart <container>). COMA creates mirrors when a container starts. - Publish the container on another host port, for example
"15432:5432"in the Compose file. - In a workspace, declare the port in
coma.yamlwith anotherlocalnumber orlocal: auto, then runcoma workspace up.
If two workspaces declare the same explicit local port, the second is refused with local_port_in_use, naming the other workspace. Choose another port or auto, or run coma workspace down in the other workspace.
On Linux, binding a local port below 1024 needs privileges. Such a mirror shows local_port_permission_denied; declare another local port for it.