# docker.el turns the Docker CLI into tablist and transient buffers in Emacs

> docker.el is an Emacs Lisp package that exposes Docker containers, images, volumes, networks, contexts and compose projects as marked lists with transient commands. It is a UI layer over the docker binary rather than a client library, which keeps it in step with the CLI but ties its behaviour to the shell it runs in.

**Silex/docker.el** — Manage docker from Emacs.

- Repository: https://github.com/Silex/docker.el
- Stars: 832 · Forks: 87
- Language: Emacs Lisp
- License: GPL-3.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/silex-docker-el

## A tablist and transient front end over the docker binary

docker.el is described as Emacs integration for Docker, and the one-line summary is accurate: it manages Docker from Emacs. The supported surface is wide. The README lists containers, images, volumes, networks, contexts and docker-compose.

The implementation approach matters more than the feature list. The package is not a Docker API client. It runs the docker binary, with the binary name coming from the docker-command variable, whose default is docker, and compose operations go through docker-compose-command, whose default is docker compose. Everything the package shows you is parsed from the output of the command line tool.

That has a clear consequence. When a new Docker feature or a new subcommand appears, the package is useful to the extent that its output is parseable and that a command exists for it. When Docker changes its output format, the package has to follow. The supported command list in the README is the contract, and it is a decent one: containers support attach, cp, diff, exec, inspect, kill, logs, pause, rename, restart, rm, start, stop and unpause, images support history, inspect, pull, push, rm, run and tag, and compose supports build, config, create, down, exec, logs, pause, pull, push, remove, restart, run, start, stop, unpause and up.

The repository layout follows the resource model. There is one file per resource type, docker-container.el, docker-image.el, docker-volume.el, docker-network.el and docker-context.el, plus docker-core.el and docker-utils.el for shared behaviour, docker-group.el for the resource picker, docker-faces.el for faces and docker-process.el for process handling. There is also a docker-compose.el file for the compose support and a test directory.

The licence is GPL-3.0, with a COPYING file at the root.

## Marking items and acting on them in bulk

The interaction model is the interesting part, and it comes from tablist rather than from Docker.

You invoke M-x docker, choose a resource, and you land in a buffer of items you can mark. The README lists the keybindings in a table, and they are worth reading as a set.

The bindings are short and worth learning by hand. The question mark lists the available actions, l configures the listing, m marks an item, u unmarks it, t toggles marks and U unmarks everything. s sorts the listing, the asterisk followed by r marks items by regexp, the less-than and greater-than keys shrink and enlarge a column, and C-c C-e exports the listing to csv.

The capability that changes the workflow is marking by regexp. Selecting twenty containers whose names match a pattern and then acting on them is a single operation, and the same marking machinery then feeds the transient prefix that runs the command.

The README points at the tablist project for more marking possibilities, which tells you where the feature set comes from. This is not a bespoke UI, it is tablist with Docker columns.

After marking, you select an action and follow the instructions. The transient prefix collects the arguments for the operation, and the transient package is what lets those arguments be set temporarily or permanently.

Export to csv is a smaller feature worth noting, since pulling a resource inventory out of the editor into a spreadsheet is useful for a capacity review and would otherwise mean piping the CLI output by hand.

## Transient arguments, repeated options and hidden suffixes

Because the package builds on transient, all transient arguments can be set temporarily or permanently, and the README links the transient manual on saving values for the mechanism.

There are hidden items too. On M-x docker you can specify the host or TLS settings, and the transient manual on enabling and disabling suffixes explains how those hidden arguments appear. This is a small detail with a real consequence: connecting to a remote Docker daemon is a configuration choice made in the transient rather than an environment variable you have to set in your shell profile.

The prompt behaviour for repeated options is documented carefully. Options that docker accepts several times, listed as -e, -v, --mount, -p, --network, --filter and compose --file, take all their values in one prompt separated by a pipe character, so the values themselves may contain commas.

```bash
Mount volume (separate with |): type=bind,source=/data,target=/data | type=volume,source=cache,target=/cache
```

That is a good example of a UI decision that follows the tool. Docker itself takes repeated flags, but a comma separated prompt would break the mount syntax, so the package uses a separator that cannot appear in the values. Pressing the key of an option that is already set opens its current value for editing, and clearing the input unsets it.

Each kind of value also keeps its own history, reachable with M-p and M-n, shared between the transients that use it and saved with the other transient histories.

## Customisation variables for the parts you will actually change

The README lists a long table of defcustom variables with their defaults, and reading it tells you which decisions the package makes for you and which it hands over.

The execution defaults are the ones that matter on day one. docker-container-exec-default-args is set to -i and -t, and docker-image-run-default-args is set to -i, -t and --rm, so a run gets an interactive terminal and is removed on exit unless you change it. docker-container-shell-file-name is /bin/sh, which is the shell you land in when you enter a container. docker-container-tramp-method has the default docker, and that is the Tramp prefix used when connecting to containers.

The column definitions and sort keys control how each listing looks. Containers sort by Image, images by Repository, contexts by Name and volumes by Driver by default, and the image history listing sorts by Created. Each resource also has its own columns variable, whose default is described in the table as too complex to show, so the useful detail is in the source rather than the README.

Two smaller settings control behaviour you might want to change. docker-show-messages decides whether docker commands print messages, and it is non-nil by default. docker-show-status controls when status is computed, with the default of local-only, which means status is computed only for local resources rather than for everything the daemon knows about.

docker-terminal-backend sets the backend for live buffers, and it defaults to auto, letting Emacs pick based on what is available.

## Failures land in *docker-diagnostics* instead of the listing buffer

The asynchronous diagnostics section describes a deliberate design decision about output streams.

The commands that list resources or discover Compose services read standard output only, so a warning on standard error cannot become a resource name. That is a real problem worth solving: if the package parsed combined output, a warning line printed by the docker binary would appear in your container list as though it were a container.

Standard error is logged in the *docker-diagnostics* buffer instead. When a command fails, the promise is rejected and the buffer gains the command, its directory, its exit status, its standard output and its standard error. The buffer is then displayed with display-buffer and docker-pop-to-buffer-action, without selecting its window, so the failure does not steal your focus.

The limitation follows directly. Separating the two streams takes a POSIX sh on the host that runs the command, and the README says that without one they stay mixed. So on a host without a POSIX shell, the separation guarantee does not hold and you are back to warnings appearing in listings.

That is worth checking before you install, since it is a platform constraint rather than a configuration toggle.

## Where docker.el is the wrong tool

The first case is a workflow that lives outside Emacs. Because the package shells out to the docker binary and parses its output, a Docker operation performed in a terminal is not visible to it and an operation performed in Emacs is not visible to your shell history or to other tooling. Teams that script their container operations will find the package useful for inspection and light management, and unhelpful for automation.

The second case is a Windows host without a POSIX shell, for the diagnostics reason described above. The README's own text makes the dependency explicit.

The third is anyone who needs a Docker client library to build an application on top of Docker. The package is an interface, and its scope stops at the commands enumerated in the README. Notably absent from that list are commands for networks and volumes beyond rm, and for contexts a small set of inspect, ls, rm and use. If you need to create a network or a volume from Emacs, this package does not offer it.

The fourth is a container workflow that needs remote filesystem browsing. The README says you can enter dired or open a file inside a container or volume, which is the Tramp integration, and Tramp in turn needs a working method for your backend. That path is convenient when it works and is another moving part when it does not.

## How it compares with the plain docker CLI

The realistic alternative is the command line, and it is a strong one, because everything docker.el does is something the docker CLI already does.

The difference is entirely in how you select and act. The CLI is a pipeline of text: you type a subcommand, you pipe into grep or jq, you write a shell loop to stop five containers. docker.el turns the same objects into a table you can mark, sort, narrow with a regexp and act on. The regexp marking is the feature with no command line equivalent worth writing, because the shell version of it is a script you would otherwise maintain.

There is also a difference in where the state lives. Emacs buffers give you a persistent, re-sorted view of the current state that you can come back to, and the transient prefix collects arguments with history, so repeatedly running the same operation does not mean retyping the same flags. A terminal gives you neither, and gives you composability in return.

The honest framing is that docker.el is a comfortable interface for the operations it covers, not a replacement for the CLI. The compose commands, the container exec with the -i -t defaults, the ability to open dired inside a container: these are conveniences layered on top of a binary that continues to be the source of truth. Anyone whose whole Docker life is a shell will get on with the CLI, and anyone who lives in Emacs will find the buffers less annoying than the pipelines.

For maintenance, the repository has a CHANGELOG.md, an Eask file for packaging, an AGENTS.md, a CI workflow badge in the README, and no GitHub releases at all. The last push to the repository was on 2026-10-16. If you rely on it, follow the MELPA package rather than a pinned commit, since the package is distributed that way.

## Conclusion

docker.el fits an Emacs user who already works with tablist and transient and wants container, image and compose operations to live in the editor instead of a terminal, particularly for marking several items at once and for entering a container shell or a dired session without leaving Emacs. It does not fit a user who wants a Docker client library, since the package shells out to the docker binary, and it does not fit a Windows workflow, because separating standard output from standard error for the diagnostic buffer needs a POSIX sh on the host. Before adopting it, install the package through MELPA, confirm the docker-command and docker-compose-command variables point at the binaries you want, and check that a POSIX shell is available if you intend to rely on the *docker-diagnostics* buffer to report failures.

## FAQ

### How do I install docker.el in Emacs?

The recommended way is through MELPA. A use-package configuration looks like (use-package docker :ensure t :bind ("C-c d" . docker)), which binds C-c d to the docker command and installs the package if it is missing.

### What can I do with docker.el?

It supports containers, images, volumes, networks, contexts and docker-compose. Containers can be attached to, copied from, inspected, killed, logged, paused, renamed, restarted and removed, images can be pulled, pushed, tagged, run and inspected, and compose projects can be built, brought up and down.

### How do I mark several Docker items at once in docker.el?

Use the tablist keybindings: m marks an item, u unmarks it, t toggles marks, U unmarks all, and * r marks items by regexp. The README also points at the tablist project for more marking possibilities, and C-c C-e exports the listing to csv.

### Where do I see errors from a failed Docker command in docker.el?

Standard error is logged in the *docker-diagnostics* buffer, and when a command fails the buffer gains the command, its directory, its exit status, its standard output and its standard error, then is displayed with display-buffer and docker-pop-to-buffer-action without selecting its window. Separating the two output streams requires a POSIX sh on the host.

### How do I change the arguments Docker runs with in docker.el?

Each transient argument can be set temporarily or permanently. The docker-command variable sets the binary to use, defaulting to docker, and docker-compose-command defaults to docker compose. Options docker accepts several times, such as -e, -v, --mount, -p, --network, --filter and compose --file, take all values in one prompt separated by a pipe character.

## Sources

- [Issues](https://github.com/Silex/docker.el/issues)
- [License: GPL-3.0](https://github.com/Silex/docker.el/blob/main/LICENSE)
- [README](https://github.com/Silex/docker.el/blob/main/README.md)
- [Silex/docker.el on GitHub](https://github.com/Silex/docker.el)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/silex-docker-el
