-
Notifications
You must be signed in to change notification settings - Fork 4
CLI Machines Docker
ed machines docker is the parsed view of docker on another machine. Every verb
here opens the shared SSH connection, runs one real docker command with
--format '{{json .}}' where docker offers it, and turns the answer into stable
fields, so a script never has to scrape a column layout. It is the same set of
operations the app's Docker window performs, running the same commands.
Nothing is installed on the far side and nothing is proxied through the Edith
app: this is /usr/bin/ssh over the ControlMaster socket the app shares, so
these commands work with Edith closed. What they need is docker on the machine
and a user who can reach its socket.
There are two ways into docker on a machine, and the difference matters.
ed machines <machine> docker ps is this page: parsed, --json, stable keys.
ed <machine> docker ps is the raw shorthand, which sends the line to the
remote shell verbatim and gives you docker's own output and exit code. Reach for
the raw form for anything this page does not cover, ed tuf docker buildx ls
being the usual example.
| Command | What it does |
|---|---|
ed machines docker ps |
Lists containers merged with live CPU and memory. Runs when you name no subcommand. |
ed machines docker images |
Lists images with their size and whether they are dangling. |
ed machines docker volumes |
Lists volumes with their driver and mountpoint. |
ed machines docker networks |
Lists networks with their driver and scope. |
ed machines docker df |
Disk usage by object type, with what is reclaimable. |
ed machines docker logs |
Streams one container's logs, with timestamps. |
ed machines docker inspect |
Prints docker's own inspect JSON, untouched. |
ed machines docker start |
Starts a container. |
ed machines docker stop |
Stops a container, with a 10 second grace period. |
ed machines docker restart |
Restarts a container, with a 10 second grace period. |
ed machines docker rm |
Removes a container, killing it first. Destructive, and there is no --yes. |
ed machines docker pause |
Freezes a container's processes. |
ed machines docker unpause |
Lets a frozen container run again. |
ed machines docker rmi |
Removes an image. Destructive. Aliased remove-image. |
ed machines docker volume-rm |
Removes a volume and the data in it. Destructive, needs --yes. |
ed machines docker prune |
Reclaims space from unused objects. Destructive, needs --yes. |
ed machines docker compose ls |
Lists compose projects. Runs when you name no compose subcommand. Aliased list. |
ed machines docker compose up |
Brings a project up in the background. |
ed machines docker compose down |
Takes a project down, removing its containers and networks. Destructive. |
ed machines docker compose restart |
Restarts a project. |
ed machines docker compose pull |
Pulls the images a project uses. |
ed machines docker compose logs |
Streams the whole project's logs. |
Five verbs remove something, and only volume-rm and prune ask first. Read
this table before you script any of them.
| Command | What disappears | Guard |
|---|---|---|
ed machines docker rm |
The container, killed first with docker rm -f. Anything written inside it and not in a volume goes with it. |
none |
ed machines docker rmi |
The image. With --force, even while a container still refers to it. |
none |
ed machines docker volume-rm |
The volume and every byte in it. This is where databases live. | --yes |
ed machines docker prune volumes |
Every volume no container currently uses, and their contents. | --yes |
ed machines docker prune images |
Every image no container uses, not only the dangling ones: the command is docker image prune -af. |
--yes |
ed machines docker compose down |
The project's containers and its network. Named volumes survive, because -v is never passed. |
none |
prune system, prune networks and prune builder remove stopped containers,
unused networks, dangling images and build cache. docker system prune -f is
what runs for system, without --volumes, so volume data is never caught by
it. prune and volume-rm without --yes report what they would do, change
nothing, and exit 0.
Lists containers, merging docker ps -a with a one-shot docker stats so each
row carries live CPU and memory next to its state and ports. It is the group's
default subcommand, so ed machines docker <machine> runs it.
ed machines docker ps [--json] [--all] <machine>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to ask. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the table. Long form only, there is no -j. |
--all, -a
|
flag | off | Include containers that are not running. Without it only running and restarting containers are listed. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
The table prints its headings even when nothing matches:
$ ed machines tuf docker ps
ID NAME IMAGE STATE CPU PORTS
$ ed machines tuf docker ps --all
ID NAME IMAGE STATE CPU PORTS
b556d7fef23e lobe-chat lobehub/lobe-chat:latest exited -
f8968a8b81e5 open-webui ghcr.io/open-webui/open-webui:main exited -
47e37ace9821 noveum-local-db-postgres-1 postgres:17-alpine exited -
efe6aaaae124 noveum-local-db-clickhouse-1 clickhouse/clickhouse-server:24.12 exited -
5477a5a28510 noveum-local-db-redis-1 redis/redis-stack:latest exited -
With the same containers running, the CPU and PORTS columns fill in and the
stopped rows are gone:
$ ed machines tuf docker ps
ID NAME IMAGE STATE CPU PORTS
b556d7fef23e lobe-chat lobehub/lobe-chat:latest running 0.0%
f8968a8b81e5 open-webui ghcr.io/open-webui/open-webui:main running 0.3% 3000 → 8080/tcp
47e37ace9821 noveum-local-db-postgres-1 postgres:17-alpine running 2.7% 5433 → 5432/tcp
CPU reads - when docker stats had nothing to say about that container,
which is every stopped container and, briefly, one that has just started.
A top-level array, one object per container, in the order docker listed them. This is a real document trimmed to one entry:
[
{
"command": "\"docker-entrypoint.sh postgres\"",
"composeProject": "noveum-local-db",
"composeService": "postgres",
"cpuPercent": null,
"createdAt": "2026-08-05 22:28:30 +0530 IST",
"health": "none",
"id": "47e37ace98211bfcf5d14f4f6e80e4d76b09c30914cb8e8ecf7e14cc029f237e",
"image": "postgres:17-alpine",
"memLimitBytes": null,
"memUsedBytes": null,
"name": "noveum-local-db-postgres-1",
"names": [
"noveum-local-db-postgres-1"
],
"ports": [],
"shortID": "47e37ace9821",
"state": "exited",
"status": "Exited (0) 2 hours ago"
}
]What the fields mean:
-
idis the full container id, becausedocker psruns with--no-trunc.shortIDis its first twelve characters, which is what the table prints and what every other verb here accepts. -
nameis the first ofnames;namesholds all of them, since a container can carry several. A container with no name at all falls back toshortID. -
stateis one ofcreated,running,paused,restarting,exited,dead,removing, orunknownwhen docker reports something newer than that list.statusis docker's own sentence, such asExited (0) 2 hours ago. -
healthisnone,starting,healthyorunhealthy. It comes from docker'sHealthStatusfield, falling back to reading(healthy),(unhealthy)orhealth: startingout ofstatus. A container with no health check reportsnone. -
portsis an array of strings, each rendered as5433 → 5432/tcpwith a literal arrow, or as5432/tcpalone when the port is exposed but not published. Do not expect->. Duplicate IPv4 and IPv6 mappings are collapsed into one entry, and the list is sorted by host port, with unpublished ports last. -
composeProjectandcomposeServicecome from thecom.docker.compose.projectandcom.docker.compose.servicelabels, and arenullon a container compose did not create. -
createdAtis docker's own string,2026-08-05 22:28:30 +0530 IST. It is not ISO 8601, unlike dates elsewhere ined --json. -
commandis docker's quoted form, so the value usually contains its own quotation marks. -
cpuPercent,memUsedBytesandmemLimitBytescome fromdocker stats --no-streamand arenullfor anything not running.cpuPercentis docker's own figure, summed across cores, so a busy container reads above 100.
ed machines tuf docker ps
ed machines tuf docker ps --all --json
ed machines tuf docker ps --json | jq -r '.[] | select(.health == "unhealthy") | .name'
ed machines tuf docker ps --json | jq -r '.[] | "\(.name) \(.ports | join(","))"'
Read only. The remote command is docker ps -a --no-trunc --format '{{json .}}'
followed by docker stats --no-stream, sent as one line with a separator
between them, with a 45 second ceiling. docker stats has its stderr thrown
away, so its noise never lands in the parse, and a container it says nothing
about is still listed with its stats null. Its exit status is the status of the
whole line, though, so a docker stats that fails outright takes the container
list down with it and exits 1.
--all is a client-side filter, not docker ps without -a: the machine
always returns every container and ed drops the ones that are neither
running nor restarting. The consequence worth remembering is that a paused
container does not appear without --all.
Lists the images on the machine with their size.
ed machines docker images [--json] <machine>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to ask. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the table. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
$ ed machines tuf docker images
ID IMAGE SIZE
e97bf9531916 ghcr.io/open-webui/open-webui:main 5.1 GB
de3a4eab8fdf postgres:16-alpine 294 MB
93aa428db0ae postgres:17-alpine 297 MB
7ef7a41df1e0 lobehub/lobe-chat:latest 617 MB
9eafe528d67a redis/redis-stack:latest 895 MB
af182398db7c docker.elastic.co/kibana/kibana:9.0.0 1.2 GB
6cec5391a4c7 docker.elastic.co/elasticsearch/elasticsearch:9.0.0 1.4 GB
A top-level array, one object per image:
[
{
"createdSince": "12 days ago",
"dangling": false,
"id": "sha256:e97bf95319168ab7fdfc5bd1e869f6a1cf6349bdf6d3e8fe16c733d2ca473491",
"repository": "ghcr.io/open-webui/open-webui",
"shortID": "e97bf9531916",
"sizeBytes": 5090000000,
"tag": "main"
},
{
"createdSince": "4 weeks ago",
"dangling": false,
"id": "sha256:de3a4eab8fdfa507ea92aac488b916b08089e515db49b055fe71dfa271ba3a28",
"repository": "postgres",
"shortID": "de3a4eab8fdf",
"sizeBytes": 294000000,
"tag": "16-alpine"
}
]-
idkeeps docker'ssha256:prefix;shortIDstrips it and keeps twelve characters, which is what the table shows and whatrmitakes. -
danglingis true when eitherrepositoryortagis<none>, and the table prints such a row as<none>:<none>. -
sizeBytesis docker's human size parsed back into bytes. Docker prints decimal units, so5.09GBbecomes5090000000rather than a byte-exact figure, and the table then re-renders it as5.1 GB. -
createdSinceis docker's relative phrase, not a date.
ed machines tuf docker images
ed machines tuf docker images --json | jq -r '.[] | select(.dangling) | .shortID'
ed machines tuf docker images --json | jq 'map(.sizeBytes) | add'
Read only, 45 second ceiling. The remote command is
docker images --no-trunc --format '{{json .}}', with no -a, so intermediate
build layers are not listed. Dangling images are, and prune images takes them
along with every other image no container uses.
Lists the volumes on the machine.
ed machines docker volumes [--json] <machine>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to ask. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the table. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
$ ed machines tuf docker volumes
NAME DRIVER MOUNTPOINT
7a438a4d027cfca045e0fcb4caa787c1e26e70ef7839917dce8b768da5a4fc38 local /var/lib/docker/volumes/7a438a4d027cfca045e0fcb4caa787c1e26e70ef7839917dce8b768da5a4fc38/_data
crowdvolt_postgres_data local /var/lib/docker/volumes/crowdvolt_postgres_data/_data
noveum-local-db_postgres_data local /var/lib/docker/volumes/noveum-local-db_postgres_data/_data
open-webui local /var/lib/docker/volumes/open-webui/_data
pg_data local /var/lib/docker/volumes/pg_data/_data
[
{
"containers": null,
"driver": "local",
"inUse": false,
"mountpoint": "/var/lib/docker/volumes/pg_data/_data",
"name": "pg_data",
"sizeBytes": null
}
]sizeBytes, containers and inUse are part of the shape the app's Docker
window fills in, and the CLI never fills them: it runs docker volume ls only,
never docker system df -v, so sizeBytes and containers are always null
and inUse is always false. The keys are present rather than dropped, so the
document shape does not change between runs. For real volume sizes use
ed machines docker df, which reports the total and reclaimable figures for
Local Volumes.
ed machines tuf docker volumes
ed machines tuf docker volumes --json | jq -r '.[].name'
ed machines tuf docker volumes --json | jq -r '.[] | select(.name | startswith("noveum")) | .mountpoint'
Read only, 45 second ceiling, docker volume ls --format '{{json .}}'. An
anonymous volume is listed under its 64 character hash, which is exactly the
name volume-rm wants.
Lists the docker networks on the machine.
ed machines docker networks [--json] <machine>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to ask. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the table. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
$ ed machines tuf docker networks
NAME DRIVER SCOPE
bridge bridge local
host host local
none null local
noveum-local-db_default bridge local
[
{
"driver": "bridge",
"id": "5b5aedee9cc4",
"name": "bridge",
"scope": "local"
},
{
"driver": "bridge",
"id": "a3f0be1c77d2",
"name": "noveum-local-db_default",
"scope": "local"
}
]id is the truncated twelve character id docker prints for networks, not the
full one: unlike ps and images, this command does not pass --no-trunc.
ed machines tuf docker networks
ed machines tuf docker networks --json | jq -r '.[] | select(.driver == "bridge") | .name'
Read only, 30 second ceiling, docker network ls --format '{{json .}}'. The
three built-in networks, bridge, host and none, are always listed and are
never touched by prune networks.
Reports docker's disk usage by object type, and how much of it is reclaimable.
ed machines docker df [--json] <machine>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to ask. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the table. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
$ ed machines tuf docker df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 12 5 11.3 GB 3.6 GB
Containers 5 0 462 MB 462 MB
Local Volumes 10 4 11.7 GB 273 MB
Build Cache 0 0 0 B 0 B
[
{
"active": 5,
"reclaimableBytes": 3585000000,
"sizeBytes": 11310000000,
"total": 12,
"type": "Images"
},
{
"active": 4,
"reclaimableBytes": 272700000,
"sizeBytes": 11730000000,
"total": 10,
"type": "Local Volumes"
}
]-
typeis docker's own label:Images,Containers,Local VolumesandBuild Cache, capitalised and spaced exactly like that. -
totalis docker'sTotalCountandactiveis how many of them are in use. -
sizeBytesandreclaimableBytesare parsed from docker's decimal strings. Docker prints reclaimable as3.585GB (31%); the percentage is dropped and only the size is kept.
This is the report to read before pruning. reclaimableBytes for Images is
what prune images would free, and the Local Volumes row is what
prune volumes would free, which is data rather than cache.
ed machines tuf docker df
ed machines tuf docker df --json | jq -r '.[] | "\(.type) \(.reclaimableBytes)"'
ed machines tuf docker df --json | jq 'map(.reclaimableBytes) | add'
Read only, 45 second ceiling, docker system df --format '{{json .}}'. On a
busy daemon this is the slowest of the read commands, because docker walks the
image and volume trees to answer it.
Streams one container's logs to your terminal.
ed machines docker logs [--tail <n>] [--follow] <machine> <container>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to ask. |
<container> |
container name or id, full or short | required | Which container's logs to read. Passed to docker as given. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--tail |
integer, 0 or more | 200 |
How many trailing lines to show. 0 shows none, which is what you want with --follow. |
--follow, -f
|
flag | off | Keep streaming until interrupted. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
There is no --json here, and no --since or --until: this is a passthrough
of docker's own output.
$ ed machines tuf docker logs lobe-chat --tail 3
2026-08-08T07:38:18.342112977Z Warning: Cannot polyfill `DOMMatrix`, rendering may be broken.
2026-08-08T07:38:18.342116199Z Warning: Cannot polyfill `ImageData`, rendering may be broken.
2026-08-08T07:38:18.342119077Z Warning: Cannot polyfill `Path2D`, rendering may be broken.
ed machines tuf docker logs lobe-chat
ed machines tuf docker logs tuf-api --tail 50
ed machines tuf docker logs open-webui --tail 0 --follow
The remote command is
docker logs --timestamps --tail <n> [--follow] <container>. Timestamps are
always on and cannot be turned off, which is the one way this differs from
typing docker logs yourself.
Output is streamed line by line as it arrives, with the container's stdout going
to your stdout and its stderr going to your stderr, so redirecting one does not
swallow the other. There is no timeout: --follow runs until you interrupt it
or the container stops.
This is one of the two verbs on this page that propagate the remote exit code
instead of mapping it into the 0 to 4 table. A container that does not exist is
docker's error, on stderr, with docker's status, usually 1. --tail is
validated before anything is sent: a negative value exits 2, though you have to
write --tail=-1 to reach the check because --tail -1 is read as a missing
value and exits 2 for that reason instead.
$ ed machines tuf docker logs open-webui --tail=-1
error: --tail cannot be negative
hint: pass 0 or more
Prints docker's own inspect output for a container, untouched.
ed machines docker inspect <machine> <container>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to ask. |
<container> |
container name or id | required | What to inspect. Passed to docker inspect as given. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
This command has no --json flag, because all of its output already is JSON:
docker's array of one object, printed exactly as docker produced it, with
docker's own key names and key order.
$ ed machines tuf docker inspect lobe-chat | head -8
[
{
"Id": "b556d7fef23e992287fe837df535b4dcfbdf2aaa48f90ee9f96fdc994ed5d79d",
"Created": "2026-08-06T22:20:40.507261889Z",
"Path": "/bin/node",
"Args": [
"/app/startServer.js"
],
ed machines tuf docker inspect lobe-chat
ed machines tuf docker inspect lobe-chat | jq -r '.[0].State.Status'
ed machines tuf docker inspect lobe-chat | jq -r '.[0].Config.Env[]'
The remote command is docker inspect <container> 2>/dev/null, with a 30 second
ceiling. Because it is plain docker inspect, it answers for any docker object,
so an image name, a volume or a network works here too even though the argument
is called container.
The two failure paths are worth telling apart. When docker itself fails, which
is what a missing container does, the non-zero status is reported and the
command exits 1; docker's stderr was discarded by the 2>/dev/null, so the hint
falls back to what landed on stdout, which for a missing object is []:
$ ed machines tuf docker inspect nosuch-container
error: docker inspect nosuch-container 2>/dev/null exited 1 on Asus TUF 7
hint: []
When docker succeeds but prints nothing at all, ed reports
no container named <container> and exits 3.
Starts a stopped container.
ed machines docker start [--json] <machine> <container>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to act on. |
<container> |
container name or id | required | Which container to start. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the one-line confirmation. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
Human output is one line, start <container>.
{
"action": "start",
"container": "open-webui",
"machine": "Asus TUF 7"
}machine is the machine's display name as Edith stores it, not what you typed.
container is echoed back exactly as you typed it, so passing a short id gives
you a short id here.
ed machines tuf docker start open-webui
ed machines tuf docker start b556d7fef23e --json
Runs docker start <container> with a 120 second ceiling. A docker that refuses
exits 1, with docker's own stderr as the hint:
$ ed machines tuf docker start nosuch-container
error: docker start failed on Asus TUF 7
hint: Error response from daemon: No such container: nosuch-container
failed to start containers: nosuch-container
This is the Docker window's start button, running the same command.
Stops a running container.
ed machines docker stop [--json] <machine> <container>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to act on. |
<container> |
container name or id | required | Which container to stop. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the one-line confirmation. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
{
"action": "stop",
"container": "open-webui",
"machine": "Asus TUF 7"
}ed machines tuf docker stop open-webui
ed machines tuf docker stop open-webui --json
Runs docker stop -t 10 <container>, so the container gets ten seconds to exit
on its own before docker kills it. The whole call has a 120 second ceiling.
Stopping a container that is already stopped is docker's business and succeeds
quietly. Failure exits 1 with docker's stderr as the hint.
Restarts a container.
ed machines docker restart [--json] <machine> <container>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to act on. |
<container> |
container name or id | required | Which container to restart. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the one-line confirmation. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
{
"action": "restart",
"container": "noveum-local-db-postgres-1",
"machine": "Asus TUF 7"
}ed machines tuf docker restart noveum-local-db-postgres-1
ed machines tuf docker restart open-webui --json
Runs docker restart -t 10 <container>, the same ten second grace period stop
uses, under the same 120 second ceiling. A container that takes longer than the
ceiling to come back leaves ed reporting a failure while docker carries on;
check with ed machines docker ps rather than assuming the restart was lost.
Removes a container, killing it first.
ed machines docker rm [--json] <machine> <container>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to act on. |
<container> |
container name or id | required | Which container to remove. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the one-line confirmation. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
{
"action": "rm",
"container": "open-webui",
"machine": "Asus TUF 7"
}ed machines tuf docker rm open-webui
ed machines tuf docker rm b556d7fef23e --json
The remote command is docker rm -f <container>, so this kills a running
container and removes it in one step rather than refusing to touch it. There is
no --yes on this verb: it acts immediately, on the first try. The container's
writable layer goes with it, and anything the container wrote outside a volume
or a bind mount is gone. Named volumes survive, because -v is never passed.
Runs under a 120 second ceiling. Failure exits 1 with docker's stderr as the hint. This is the Docker window's remove button, running the same command.
Freezes a container's processes without stopping it.
ed machines docker pause [--json] <machine> <container>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to act on. |
<container> |
container name or id | required | Which container to freeze. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the one-line confirmation. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
{
"action": "pause",
"container": "open-webui",
"machine": "Asus TUF 7"
}ed machines tuf docker pause open-webui
ed machines tuf docker pause open-webui --json
Runs docker pause <container> under a 120 second ceiling. The container keeps
its memory and its ports; its processes simply stop being scheduled.
A paused container reports state: "paused", which ed machines docker ps
counts as not running, so it vanishes from a bare ps and only reappears with
--all. Pausing something already paused is an error on docker's side and exits
1.
Lets a frozen container run again.
ed machines docker unpause [--json] <machine> <container>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to act on. |
<container> |
container name or id | required | Which container to resume. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the one-line confirmation. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
{
"action": "unpause",
"container": "open-webui",
"machine": "Asus TUF 7"
}ed machines tuf docker unpause open-webui
ed machines tuf docker unpause open-webui --json
Runs docker unpause <container> under a 120 second ceiling. Unpausing a
container that is not paused exits 1 with docker's complaint as the hint.
Removes an image. Also answers to remove-image.
ed machines docker rmi [--json] [--force] <machine> <image>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to act on. |
<image> |
image name, repository:tag, or an id from ed machines docker images
|
required | Which image to remove. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the one-line confirmation. |
--force |
flag | off | Remove it even when a container still refers to it. Adds -f to the docker command. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
Human output is one line, removed image <image>.
{
"forced": false,
"image": "postgres:16",
"machine": "Asus TUF 7"
}forced records whether you passed --force, not whether force was needed.
image is echoed back as you typed it.
ed machines tuf docker rmi postgres:16
ed machines tuf docker rmi de3a4eab8fdf --force
ed machines tuf docker remove-image postgres:16 --json
Runs docker image rm [-f] <image> under a 120 second ceiling. There is no
--yes on this verb, which is deliberate: an image is re-pullable, unlike a
volume.
Without --force, docker refuses to remove an image a container still refers
to, even a stopped one, and that refusal becomes exit 1 with docker's message as
the hint. With --force docker untags it and removes it when nothing else holds
the layers. Removing an image that several tags point at removes only the tag
you named unless you pass an id.
This is the Docker window's image delete button, which always runs the unforced form.
Removes a volume and everything in it. Does nothing without --yes.
ed machines docker volume-rm [--json] [--yes] <machine> <volume>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to act on. |
<volume> |
volume name, exactly as ed machines docker volumes prints it |
required | Which volume to remove. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the human lines. |
--yes |
flag | off | Actually remove it. Without this nothing is touched. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
Without --yes the command says what it would do and exits 0. The refusal note
goes to stderr, so a script reading stdout sees only the plan:
$ ed machines tuf docker volume-rm pg_data
would remove volume pg_data and everything in it
nothing was removed; pass --yes to go ahead
The same three keys in both directions. removed is the one that changes:
{
"machine": "Asus TUF 7",
"removed": false,
"volume": "pg_data"
}With --yes, and after docker agreed:
{
"machine": "Asus TUF 7",
"removed": true,
"volume": "pg_data"
}ed machines tuf docker volume-rm pg_data
ed machines tuf docker volume-rm pg_data --json
ed machines tuf docker volume-rm pg_data --yes
A volume is where a container keeps the data it means to survive a restart, so
this is the one container operation with nothing behind it: no trash, no undo,
no copy on the machine. --yes exists for that reason, and the dry run is the
default rather than an option.
With --yes the remote command is docker volume rm <volume>, under a 120
second ceiling. Docker refuses to remove a volume a container still refers to,
even a stopped one, and that refusal is exit 1 with docker's message as the
hint; remove or recreate the container first. There is no force flag here.
The dry run is not free: ed still opens the connection and checks that docker
is usable before it prints the plan, so volume-rm without --yes against an
unreachable machine exits 4 rather than 0.
Reclaims space by removing unused docker objects. Does nothing without --yes.
ed machines docker prune [--json] [--yes] <machine> [<what>]
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to act on. |
<what> |
images, volumes, networks, builder or system
|
system |
Which family of unused objects to remove. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the human lines. |
--yes |
flag | off | Actually prune. Without it nothing is removed. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
Each target maps to exactly one docker command, and the dry run prints it:
<what> |
Command | What goes |
|---|---|---|
images |
docker image prune -af |
Every image no container uses, not just the dangling ones. |
volumes |
docker volume prune -f |
Every volume no container uses, and the data in it. |
networks |
docker network prune -f |
Every user-defined network nothing is attached to. |
builder |
docker builder prune -af |
The whole build cache. |
system |
docker system prune -f |
Stopped containers, unused networks, dangling images and build cache. Volumes are not included. |
$ ed machines tuf docker prune
would run: docker system prune -f
pass --yes to do it
The two shapes differ, which is worth knowing before you parse them. The dry run reports the command it would have run:
{
"applied": false,
"command": "docker image prune -af",
"machine": "Asus TUF 7",
"target": "images"
}The applied run reports what docker said instead:
{
"applied": true,
"machine": "Asus TUF 7",
"output": "Total reclaimed space: 3.585GB",
"target": "images"
}command is present only when applied is false, and output only when it is
true. output is docker's stdout with leading and trailing whitespace trimmed,
which for a real prune is a list of deleted ids followed by the reclaimed total.
Without --json that same stdout is printed raw.
ed machines tuf docker prune
ed machines tuf docker prune images --json
ed machines tuf docker prune builder --yes
ed machines tuf docker prune volumes --yes
The target is checked before anything else happens, including before the connection is opened, so a typo costs nothing and exits 3 with the valid list:
$ ed machines tuf docker prune everything
error: docker cannot prune everything
hint: try: images, volumes, networks, builder, system
volumes is spelled out as its own target rather than folded into system on
purpose. docker system prune does not touch volumes unless it is asked to, and
ed never asks it to, so the only way to lose volume data here is to type
prune volumes --yes.
prune images is more aggressive than docker image prune typed by hand. The
-a means every image without a container, not only the untagged ones, so a
tagged image you pulled for later goes too. Check
ed machines docker df --json first: the Images row's reclaimableBytes is
what this will free.
With --yes the ceiling is 300 seconds, longer than any container verb here and
second only to compose pull. A prune that outruns it is reported as a failure
while docker keeps going on the machine.
Lists the compose projects on the machine. Also answers to list, and runs when
you name no compose subcommand.
ed machines docker compose ls [--json] <machine>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to ask. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the plain lines. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
Human output is one project name per line. With no projects, the message goes to stderr and stdout stays empty:
$ ed machines tuf docker compose ls
no compose projects on Asus TUF 7
A top-level array of strings, and [] rather than nothing when there are none:
[
"noveum-local-db"
]Only the project name is reported. The status and config file path that
docker compose ls also prints are parsed away.
ed machines tuf docker compose ls
ed machines tuf docker compose list --json
ed machines tuf docker compose ls --json | jq -r '.[]'
Read only, 30 second ceiling, docker compose ls --format json 2>/dev/null.
Note the missing -a: docker lists only projects that are currently running, so
a project whose containers are all stopped does not appear here even though its
containers still exist. That is not cosmetic, because every other compose verb
refuses a project this command did not list.
To see the stopped ones, ask docker directly through the raw form:
$ ed tuf 'docker compose ls -a --format json'
[{"Name":"noveum-local-db","Status":"exited(3)","ConfigFiles":"/home/pulkit/Desktop/noveum-app-nextjs/extras/db/docker-compose.local-db.yml"}]
A machine whose docker has no compose plugin fails rather than reporting
nothing. Compose's complaint is discarded by the 2>/dev/null, but its non-zero
status is not, so you get exit 1 naming the command that failed and an empty
hint:
$ ed machines old-box docker compose ls
error: docker compose ls --format json 2>/dev/null exited 1 on old-box
hint:
Brings a compose project up in the background.
ed machines docker compose up [--json] <machine> <project>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to act on. |
<project> |
compose project name, exactly as compose ls prints it |
required | Which project to bring up. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the one-line confirmation. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
Human output is one line, up -d <project>.
{
"action": "up -d",
"machine": "Asus TUF 7",
"project": "noveum-local-db"
}action carries the compose action as it was sent, so it reads up -d rather
than up.
ed machines tuf docker compose up noveum-local-db
ed machines tuf docker compose up noveum-local-db --json
The project name is checked against compose ls before anything runs, and an
unknown one exits 3 with the projects that do exist as the hint, or with a nudge
to look again when there are none:
$ ed machines tuf docker compose up noveum-local-db
error: no compose project named noveum-local-db on Asus TUF 7
hint: run `ed machines Asus TUF 7 docker compose ls` to look again
That is the common failure, and it is usually not a typo: compose ls lists
only running projects, so a project that is fully down cannot be named here.
Bring it up through the raw form, from the directory that holds its file:
ed tuf 'cd /srv/app && docker compose up -d'.
The remote command is docker compose -p <project> up -d, run from the SSH
login directory, with no -f and no --project-directory. Compose has to be
able to find the project's configuration from there; when it cannot, its own
message comes back as the hint on an exit 1. The ceiling is 300 seconds.
There is no Docker window equivalent for this verb. The window groups containers by compose project but never runs compose itself.
Takes a compose project down.
ed machines docker compose down [--json] <machine> <project>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to act on. |
<project> |
compose project name, exactly as compose ls prints it |
required | Which project to take down. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the one-line confirmation. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
{
"action": "down",
"machine": "Asus TUF 7",
"project": "noveum-local-db"
}ed machines tuf docker compose down noveum-local-db
ed machines tuf docker compose down noveum-local-db --json
The remote command is docker compose -p <project> down, with a 300 second
ceiling and the same project check up performs. down removes the project's
containers and its default network. Named volumes survive: -v is never passed,
and there is no flag here that would pass it. To remove a project's data as
well, list its volumes with ed machines docker volumes and take them with
ed machines docker volume-rm --yes.
After a successful down the project disappears from compose ls, so the
matching up through ed will not find it. That round trip is the reason to
prefer the raw form for projects you take all the way down.
Restarts a compose project.
ed machines docker compose restart [--json] <machine> <project>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to act on. |
<project> |
compose project name, exactly as compose ls prints it |
required | Which project to restart. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the one-line confirmation. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
{
"action": "restart",
"machine": "Asus TUF 7",
"project": "noveum-local-db"
}ed machines tuf docker compose restart noveum-local-db
ed machines tuf docker compose restart noveum-local-db --json
Runs docker compose -p <project> restart with a 300 second ceiling, after the
same project check. This restarts the existing containers rather than recreating
them, so a changed compose file has no effect: that needs up. The Docker
window restarts containers one at a time and never a whole project, so this verb
has no button behind it.
Pulls the images a compose project uses.
ed machines docker compose pull [--json] <machine> <project>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to act on. |
<project> |
compose project name, exactly as compose ls prints it |
required | Whose images to pull. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--json |
flag | off | Emit JSON on stdout instead of the one-line confirmation. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
{
"action": "pull",
"machine": "Asus TUF 7",
"project": "noveum-local-db"
}ed machines tuf docker compose pull noveum-local-db
ed machines tuf docker compose pull noveum-local-db --json
Runs docker compose -p <project> pull with a 900 second ceiling, the longest
on this page, because pulling several images over a slow link is the one thing
here that legitimately takes a quarter of an hour. Progress is not streamed:
compose's output is collected and discarded on success, and only the
confirmation line is printed. Pass through ed tuf 'cd /srv/app && docker compose pull' if you want to watch it.
Pulling needs the compose file, so this is the other verb, with up, that
depends on compose finding the project's configuration from the login directory.
Streams the logs of every container in a compose project.
ed machines docker compose logs [--tail <n>] [--follow] <machine> <project>
| Name | Type / values | Default | What it does |
|---|---|---|---|
<machine> |
machine name, ssh alias, id, or any unambiguous prefix | required | Which machine to ask. |
<project> |
compose project name, exactly as compose ls prints it |
required | Whose logs to stream. |
| Name | Type / values | Default | What it does |
|---|---|---|---|
--tail |
integer, 0 or more | 200 |
How many trailing lines to show, per service. |
--follow, -f
|
flag | off | Keep streaming until interrupted. |
--help, -h
|
flag | off | Print the help for this command on stdout and exit 0. |
There is no --json, and unlike ed machines docker logs there are no
timestamps: compose prefixes each line with the service name instead.
ed machines tuf docker compose logs noveum-local-db
ed machines tuf docker compose logs noveum-local-db --tail 20
ed machines tuf docker compose logs noveum-local-db --tail 0 --follow
The remote command is docker compose -p <project> logs --tail <n> [-f], after
the same project check the other compose verbs make, so an unlisted project
exits 3 before anything streams. --tail is validated first and a negative
value exits 2.
Like ed machines docker logs, this is a passthrough: stdout and stderr stay
separate, there is no timeout, and the remote exit code becomes yours.
| Code | When |
|---|---|
| 0 | The command did what it said. A dry run of prune or volume-rm also exits 0, having changed nothing, and so do --help and --version. |
| 1 | Docker ran and refused, or failed: no such container, an image still in use, a volume still attached, a compose file compose could not find. The message names the verb and the machine, and docker's own stderr is the hint. Also a remote command that outran its timeout, and a logs stream whose ssh would not start. |
| 2 |
--tail was negative, or the command line was wrong in the ordinary way: an unknown flag, a missing <machine> or <container>, a --tail that is not a number. |
| 3 | The machine name matched nothing or matched several; <what> was not one of the five prune targets; <project> was not in compose ls; inspect got a zero status and no output. |
| 4 | The machine could not be reached, docker on it is not usable (not installed, the daemon down, or this user cannot talk to the socket), or ssh itself could not be launched for a non-streaming command. |
| other |
logs and compose logs propagate the remote process's own exit code verbatim, so anything docker returns reaches you unchanged. |
The docker availability failures all read the same way, with the specific reason as the hint:
error: docker is not usable on Asus TUF 7
hint: docker is not installed there
The other hints on that message are this user cannot talk to the docker socket, The Docker daemon is not running. and, when docker answered with
something unrecognisable, docker reported an unknown state.
- Word order is free.
ed machines tuf docker psanded machines docker ps tufare the same invocation: the machine is rewritten into the position the parser expects. A subcommand name always wins, so a machine literally calleddockerhas to be named ased machines show docker. -
ed tuf docker psis not this page. Naming a machine as the first word makes the rest a raw remote command, so that line runs docker's ownpson the machine and prints docker's own table. Addmachinesto get the parsed form. This is the escape hatch for everything not covered here:ed tuf docker buildx ls,ed tuf docker exec -it api sh. -
ed machines docker <machine>with no verb isps, anded machines docker compose <machine>with no verb iscompose ls. - There is no
ed machines docker exec. The Docker window's shell button ised machines exec --tty <machine> 'docker exec -it <container> sh', and--ttyis what makes an interactive shell work at all. - Every verb, including both dry runs, starts by running
docker version --format '{{json .}}'on the machine with a 25 second ceiling and refusing to go on unless the daemon answered. That is the one round trip you pay for before anything else happens, and it is why an unreachable machine exits 4 even for a command that would have changed nothing. - Docker commands always run in the SSH login directory. The remembered
cdthated <machine> cd ...sets belongs toed machines execand does not reach this page, which is why the compose verbs pass a project name rather than a directory. - The timeouts are per command: 25 seconds for the version probe, 45 for
ps,images,volumesanddf, 30 fornetworks,inspectandcompose ls, 120 for every container lifecycle verb and forrmiandvolume-rm, 300 forpruneand forcompose up,downandrestart, 900 forcompose pull.logsandcompose logshave none. A command that outruns its ceiling has itssshsentSIGTERM, thenSIGKILLtwo seconds later, and surfaces as exit 1 while the work carries on unsupervised on the machine. -
--jsonoutput is one document per invocation, keys sorted, two space indent. Nothing on this page streams JSON, and nothing here takes--json --follow. - Three verbs have no
--jsonat all:logs,inspectandcompose logs.inspectdoes not need one, since its output is already docker's JSON. - The container id you pass is never resolved by
ed. Names, short ids and full ids all go to docker as typed, so docker's own matching rules apply, including its refusal when a short id is ambiguous. -
ps --jsondrops two fields it collects. Network rx and tx bytes are parsed out ofdocker statsand never reach the document; only CPU and memory do. - Volume sizes are never reported by
volumes. Usedf. - Every mutating verb here is claimed by a Docker window action except the four compose lifecycle verbs, which the window does not have: it groups containers by compose project but never runs compose.
- Some hints embed the machine's display name unquoted, so a machine whose name
has spaces produces a hint you cannot paste as is. Use the ssh alias,
tuf, or any unambiguous prefix instead. - These commands never need Edith to be running, and never ask macOS for a permission. Everything they touch is on the other machine.
-
ed machinesfor the machine directory itself, connecting and disconnecting, anded machines metricsfor the host the containers run on. -
Running commands on a machine for the raw form,
--tty, and everything docker can do that this page does not parse. -
ed machines filesfor the compose files and bind mounts behind these projects. -
ed machines powerfor the systemd unit that starts docker, and for the machine's own power state. -
Conventions and contracts for the exit code table and the
--jsonguarantees these commands follow. -
The
edcommand line for the rest of the reference.
Auto-generated from docs/, edit the docs in the repo, not the wiki.
CLI reference
Companion
- Deploy
- Concepts
- Concepts Memory
- Concepts Ingestion
- Concepts Search
- Concepts Chat
- Concepts Learning
- Concepts Brain
- Concepts Friend
- Hosts
- Stack
- Status
- Doctor
- Search
- Index
- Ingest
- Episodes
- Sync
- Observations
- Reflect
- Beliefs
- Ask
- Extract
- Claims
- Corroborate
- Runs
- Chat
- Conversations
- Forget
- Export
- Import
- Erase
- Wipe
- Episode
- Nightly
- Reason
- Personas
- Council
- Lenses
- Core
- Why
- Hypotheses
- Predictions
- Commitments
- Discrepancies
- Calibration
- Inquire
- Entities
- Eval
- Standup
- Machines
- Baselines
- Connectors
- Facts
- Correct
- Weekly
- Db
Guides