- Go 99.1%
- Shell 0.7%
- Makefile 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Four things the review of #304 found after it merged. The palette's first entry for a compose service had become Up. The palette opens with its first entry selected, so enter on a freshly opened palette went from a read to `compose up`, which on a `drift` row destroys and recreates the containers. Inspect is first again in both Compose views; Up leads only on an `absent` row, where it is the one entry that can act at all. |
||
| .github | ||
| app | ||
| appui | ||
| demos | ||
| docker | ||
| docs | ||
| mocks | ||
| search | ||
| snap | ||
| terminal | ||
| tls | ||
| ui | ||
| version | ||
| .gitignore | ||
| .godir | ||
| .goreleaser.yml | ||
| .travis.yml | ||
| BUILD.md | ||
| CLAUDE.md | ||
| CODE_OF_CONDUCT.md | ||
| DEBUG.md | ||
| Dockerfile | ||
| Dockerfile.goreleaser | ||
| dryup.sh | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| main.go | ||
| Makefile | ||
| README.md | ||
| RELEASE_NOTES.md | ||
| revive.toml | ||
dry
Dry is a terminal application for Docker and Docker Compose.
It lets you browse containers, images, networks, and volumes, and manage Compose projects — bring them up, take them down, see what has drifted from the compose file — without leaving the terminal. It can be used with both local or remote Docker daemons.
Besides showing information, it can be used to manage Docker. Most of the commands that the official Docker CLI provides, are available in dry with the same behaviour. A list of available commands and their keybindings can be found in dry's help screen or in this README.
It can also be used as a monitoring tool for Docker containers, and, when the daemon is running a Swarm cluster, to manage Nodes, Services, and Stacks — see Docker Swarm below.
Dry is installed as a single binary and does not require external libraries.
The demo below shows a dry session.
dry keybinds
Global
| Keybinding | Description |
|---|---|
| % | filter list |
| F1 | sort list |
| F5 | refresh list |
| F7 | toggle showing Docker daemon information |
| F8 | show docker disk usage |
| F9 | show last 10 docker events |
| F10 | show docker info |
| 1 | show container list |
| 2 | show image list |
| 3 | show network list |
| 4 | show volumes list |
| 5 | show node list (on Swarm mode) |
| 6 | show service list (on Swarm mode) |
| 7 | show stacks list (on Swarm mode) |
| 8 | show compose projects list |
| ArrowUp or k | move the cursor one line up |
| ArrowDown or j | move the cursor one line down |
| g | move the cursor to the top |
| G | move the cursor to the bottom |
| : | open command palette |
| Space | open Quick Peek for the current selection |
| Ctrl+0 | cycle color theme (dark/light) |
| q | quit dry |
Workspace mode (--workspace)
dry --workspace enables the Phase 1 workspace shell. This keeps the current list view visible together with a passive context pane and a bottom activity pane.
TabandShift+Tabswitch focus betweenNavigator,Context, andActivityin the full workspace layout.- When
Contextis focused, you can scroll it with the usual navigation keys. p/Ppins or unpins the current preview.:opens the command palette with global and context-aware actions.SpaceopensQuick Peek, a temporary side panel with recent logs or inspect/details for the current selection.ftoggles follow mode in the activity pane.- Existing primary actions stay on their original keys. For example,
Enterstill opens the container command menu and still inspects resources in views whereEnteralready meant inspect/drill-down. - Embedded activity logs start from a recent tail instead of replaying the full log history.
- On narrow or short terminals, workspace mode falls back to a compact single-pane layout and lets
Tabswitch between navigator and activity.
Container commands
| Keybinding | Description |
|---|---|
| Enter | show container command menu (includes Attach for running containers) |
| F2 | toggle on/off showing stopped containers |
| i | inspect |
| l | container logs |
| e | remove |
| s | stats |
| x | exec a command in the selected container (default /bin/sh) |
| Ctrl+e | remove all stopped containers |
| Ctrl+k | kill |
| Ctrl+r | start/restart |
| Ctrl+t | stop |
Image commands
| Keybinding | Description |
|---|---|
| i | history |
| Ctrl+d | remove dangling images |
| Ctrl+e | remove image |
| Ctrl+f | remove image (force) |
| Ctrl+u | remove unused images |
| Enter | inspect |
Network commands
| Keybinding | Description |
|---|---|
| Ctrl+e | remove network |
| Enter | inspect |
Volume commands
| Keybinding | Description |
|---|---|
| Ctrl+a | remove all volumes |
| Ctrl+e | remove volume |
| Ctrl+f | remove volume (force) |
| Ctrl+u | remove unused volumes |
| Enter | inspect |
Service commands
| Keybinding | Description |
|---|---|
| i | inspect service |
| l | service logs |
| Ctrl+r | remove service |
| Ctrl+s | scale service |
| Ctrl+u | update service |
| Enter | show service tasks |
Compose Projects commands
| Keybinding | Description |
|---|---|
| Enter | show project services |
| u | bring the selected service up, with no prompt; on a project's own row it asks first |
| d | take the project down, from its own row and behind a prompt: compose down removes the project's networks too, so there is no per-service form |
| c | show the project's rendered compose configuration |
| l | logs for the selected service, or the project on its header |
| Ctrl+t | stop the selected service, or the project on its header |
| Ctrl+r | restart the selected service, or the project on its header |
| Ctrl+e | remove the selected service's containers, or the project's |
Ctrl+t, Ctrl+r, Ctrl+e and d all name their target in a confirmation
prompt, "Stop service webapp/api?" or "Take project webapp down?", so a
cursor that has moved is visible before anything runs. u on a service row
is the one action that runs unasked, since it creates rather than destroys;
on a drift row it does recreate the containers, which is the point of the
label.
Compose Services commands
| Keybinding | Description |
|---|---|
| Enter | inspect service |
| Esc | back to projects |
| u | bring the selected service up |
| c | show the project's rendered compose configuration |
| l | service logs |
| Ctrl+s | start service containers |
| Ctrl+t | stop service containers |
| Ctrl+r | restart service containers |
| Ctrl+e | remove service containers |
Moving around buffers
| Keybinding | Description |
|---|---|
| ArrowUp or k | move the cursor one line up |
| ArrowDown or j | move the cursor one line down |
| g | move the cursor to the beginning of the buffer |
| G | move the cursor to the end of the buffer |
| n | after search, move forwards to the next search hit |
| N | after search, move backwards to the previous search hit |
| s | search |
| pg up | move the cursor "screen size" lines up |
| pg down | move the cursor "screen size" lines down |
Installation
The easiest way to install the latest binaries for Linux and Mac is to run this in a shell:
curl -sSf https://moncho.github.io/dry/dryup.sh | sudo sh
sudo chmod 755 /usr/local/bin/dry
Binaries
If you dont like to curl | sh, binaries are provided.
Mac OS X / Homebrew
If you're on OS X and want to use homebrew:
brew tap moncho/dry
brew install dry
Docker
docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock -e DOCKER_HOST=$DOCKER_HOST moncho/dry
Arch Linux
yay -S dry-bin
Usage
Open a console, type dry. It will try to connect to:
- A Docker host given as a parameter (-H).
- if none given, a Docker host defined in the $DOCKER_HOST environment variable.
- if not defined, to unix:///var/run/docker.sock.
dry does not read docker context, so with Docker Desktop, colima or
Rancher Desktop, whose active context is not that socket, name the host with
-H or $DOCKER_HOST. The same value is what dry hands the
docker compose plugin, so the two always agree about which daemon they are
talking to.
If no connection with a Docker host succeeds, dry will exit.
Connecting over SSH
dry can talk to a remote Docker daemon through SSH, the same way the docker CLI does:
DOCKER_HOST=ssh://user@host dry
Host, port, and user are resolved like the ssh command resolves them:
Hostname, Port, User, and IdentityFile directives from
~/.ssh/config are honored, the port defaults to 22, the user defaults to
the current OS user, and the remote Docker socket defaults to
/var/run/docker.sock (a different socket path can be given in the URL,
e.g. ssh://user@host:2222/run/user/1000/docker.sock).
Authentication tries, in order: the identity files configured for the host
in ~/.ssh/config (or, when none is configured, the ~/.ssh/id_* keys),
keys held by a running SSH agent (SSH_AUTH_SOCK), and a password given
in the URL. Passphrase protected key files are used through the agent.
The remote host key is verified against ~/.ssh/known_hosts and
/etc/ssh/ssh_known_hosts, and the connection fails if the host is
unknown or its key does not match. Connect once with ssh host to record
the key. Verification can be disabled with
DRY_SSH_INSECURE_SKIP_HOST_KEY_CHECK=1, which leaves the connection open
to interception and should only be used against hosts you fully control.
dry -T light launches dry with the light color theme. Available themes: dark (default), light.
dry --workspace launches the experimental Phase 1 workspace layout.
dry -p launches dry with pprof package active.
Docker Compose
Compose projects show up in their own view (key 8), each project row followed by its services indented under it; pressing Enter on a project opens its services, networks and volumes in the Compose Services view.
| Keybinding | Description |
|---|---|
| u | bring the selected service up, with no prompt; on a project's own row it asks first |
| d | take the project down, from its own row and behind a confirmation prompt |
| c | show the project's rendered compose configuration |
u and c work in both the Compose Projects and Compose Services views; d works in Compose Projects only.
Projects are discovered two ways: from container labels, including stopped
containers, and from a compose file in the directory dry was started in.
A project known only from a file is listed with no running containers, and
pressing u brings it up.
Everything compose-specific needs the docker compose CLI plugin: the
u/d/c keys, the SYNC column and that directory scan. Without it the
views still list projects found from container labels, and dry probes
once at startup, so installing it needs a restart. An empty Compose view has
three causes, and these tell them apart:
docker compose versionfails: no plugin, so no scan either.- the directory holds none of
compose.yaml,compose.yml,docker-compose.yml,docker-compose.yaml. The scan looks in the directory dry started in, never below it. docker compose configin that directory fails. A file that does not resolve, a YAML error or a required variable with no value, is dropped without a message.
The scan passes the one file it picked, so an override file beside it is not
applied and u on a scanned project creates something different from
docker compose up in the same directory. It matches an existing project by
name, and compose takes that name from the directory's basename unless the
file sets name:. A project brought up with -p therefore needs
COMPOSE_PROJECT_NAME=<name> dry to match, or the scan lists it a second
time under the directory's name.
The SYNC column reports whether a service's running containers match its compose file:
| Label | Meaning |
|---|---|
ok |
the containers were created from the compose file as it is now |
drift |
the compose file changed since the containers were created, the next u recreates them |
absent |
the compose file defines the service and dry lists no container for it |
A service with absent gets a row of its own under its project, in the same
name order as the rest, so pressing u on it brings up that one service. It
appears once the drift check has run. The keys that need a container to act
on answer "u brings it up" on that row rather than prompting for an action
with nothing to do: enter, l, ^t, ^r and ^e in both Compose views,
plus ^s in Compose Services, which is the only view that has it. u, the
palette's Up and its Force Recreate all create the service, so they run as
usual. A service behind a compose profile is not part of the file's resolved
configuration, so it gets no row until the profile is enabled, and a service
whose only container is a one-off (docker compose run) reads as absent,
since dry lists that container under neither the service nor the project.
SYNC is not HEALTH, the column immediately to its left, which is Docker's
own reading of a container: none there means the container declares no
healthcheck, and has nothing to do with the compose file. A project row's
STATUS says not created for the same idea one level up, when nothing in
the project has containers.
A blank cell is none of those. SYNC is per service, so it is always empty on a project row, and on the section, network and volume rows of the Compose Services view. On a service row it means one of:
- no plugin, or the check has not run yet;
- the project's compose file is not on this machine, see below;
- the first check for this project failed. The message bar names it once, and a later identical failure keeps the label it already had rather than banners again;
- the file no longer defines a service whose containers are still running;
- the file defines it behind a profile, which reports no hash. Start with
COMPOSE_PROFILES=dev dryto fix both that and whatubrings up.
F5 re-runs the check: the directory scan and every project's drift in Compose Projects, the selected project's resources and drift in Compose Services. A refresh that arrives while a check is already running is deferred rather than dropped, so it lands once that one finishes.
The command palette (:) adds Force Recreate, on a service row in
the Compose Services view: it recreates that service's containers even when
nothing has drifted, which u would skip.
Compose files and the machine dry runs on
u, c and Force Recreate have to read the compose file, and the plugin
reads it from the filesystem dry runs on. The paths come from the
project's com.docker.compose.project.config_files label, written by
whichever machine ran compose up, or from the directory scan. Every
recorded path has to be absolute and present there; when one is not, dry
says Project <name> has no compose file on this host and refuses before
running compose.
docker compose ls -a prints each project's recorded files, which shows
which of three cases you are in. (It talks to whatever your shell's
docker context or DOCKER_HOST points at, which is not necessarily the
daemon dry is connected to.)
- No path recorded, from an older Compose: start dry in the project's directory and the scan fills in the file it finds.
- A relative path, which an older Compose recorded as given: bring the project down and up again from its directory.
- An absolute path that is not there: the project was brought up on another machine, or the file has since moved. A project brought up from a different directory on this machine is not this case, its recorded path is absolute and still valid.
Either put the files back where the label says, or run dry on the machine
that has them. To do the latter, name the daemon with DOCKER_HOST or -H:
both reach the plugin. dry does not read docker context and always
passes the plugin a DOCKER_HOST, so with Docker Desktop or colima name the
host explicitly rather than relying on the context.
To put the files back, recreate the project directory too, not just the
compose file: dry passes the project's
com.docker.compose.project.working_dir label as --project-directory, and
relative build: contexts and bind-mount sources resolve under it. That
label is printed by:
docker inspect $(docker ps -aq --filter label=com.docker.compose.project=<name> | head -1) \
--format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}'
The paths are re-checked on every invocation, so the keys start working the moment the files are in place.
d (down) needs the plugin but no file, because compose removes a project by
its container labels, so it keeps working when the paths do not. The Ctrl+
lifecycle keys need neither: they act on containers through the Docker API,
in Compose Projects and
Compose Services.
Docker Swarm
dry also works with Docker Swarm. When the connected daemon reports an active swarm, three extra views become available: Nodes (5), Services (6), and Stacks (7). Without an active swarm these views, their keybindings, and their command-palette entries are hidden.
Contributing
All contributions are welcome.
- Fork the project.
- Make changes on a topic branch.
- Pull request.
Copyright and license
Code released under the MIT license. See LICENSE for the full license text.
Credits
Built on top of:
Alternatives
See Awesome Docker list for similar tools to work with Docker.
