What Is Emacs Docker Integration
Emacs Docker integration refers to the ecosystem of Emacs packages and built-in capabilities that allow developers to interact with Docker directly from within their editor. Instead of switching to a terminal to run docker ps, docker build, or docker-compose up, you can manage containers, images, volumes, networks, and even edit files inside running containers — all without leaving Emacs.
This integration typically spans several layers:
- Container & image management — listing, starting, stopping, and inspecting Docker resources via dedicated Emacs buffers
- Dockerfile authoring — syntax highlighting, completion, and snippets for writing Dockerfiles
- Docker Compose orchestration — managing multi-container applications through compose files
- TRAMP-based container access — browsing and editing files inside running containers as if they were local directories
- Build & run pipelines — executing builds, attaching to container shells, and viewing logs from within Emacs
The most popular packages that power this experience include docker.el (by Akira Komamura, often referred to as docker on MELPA), docker-compose.el, dockerfile-mode, and Emacs's own TRAMP subsystem with Docker-specific methods.
Why Docker Integration in Emacs Matters
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →For developers who live in Emacs, context switching is the enemy of productivity. Every time you leave the editor to run a Docker command in a terminal, you break your flow, lose your place, and spend cognitive energy reorienting yourself. Docker integration eliminates these context switches entirely.
Here are the concrete benefits:
- Single environment for all development tasks — write code, manage containers, view logs, and debug issues without leaving your editor
- Keyboard-driven efficiency — Emacs keybindings apply uniformly across Docker operations, enabling rapid navigation and action
- Persistent, searchable state — Docker resource listings appear in regular Emacs buffers where you can search, filter, bookmark, and even apply occur/occur-edit
- Seamless file editing inside containers — TRAMP lets you open
/docker:container-name:/etc/nginx/nginx.confand edit it with all your Emacs modes active - Integrated REPL-like workflow — attach to a container shell via
M-x docker-runand interact with it in a comint-based buffer
Whether you are debugging a misbehaving microservice, iterating on a Dockerfile, or managing a complex compose stack, having Docker at your fingertips inside Emacs transforms the experience from fragmented to fluid.
Core Packages Overview
Before diving into usage, let's survey the key packages you'll want to install. All are available on MELPA:
docker.el(package namedocker) — the central hub: lists images, containers, volumes, networks, and provides transient-based menus for actionsdocker-compose.el— manage Docker Compose services directly from Emacsdockerfile-mode— major mode for editing Dockerfiles with syntax highlighting, completion, and snippetsdocker-tramp.el(optional) — extends TRAMP with convenient Docker syntax; often built into modern TRAMP
With these installed, your Emacs becomes a full-fledged Docker management console.
Setting Up docker.el
The docker.el package is the cornerstone of Emacs Docker integration. Install it via MELPA, then add a minimal configuration to your init file:
(use-package docker
:ensure t
:bind ("C-c d" . docker-menu))
Alternatively, for a more comprehensive setup with transient menus (which the package uses extensively):
(use-package docker
:ensure t
:config
;; Enable transient menu for quick access
(define-key docker-menu-mode-map (kbd "?") 'docker-help)
;; Refresh intervals for live buffers (in seconds)
(setq docker-container-refresh-interval 5)
(setq docker-image-refresh-interval 10)
:bind
("C-c d" . docker-menu)
("C-c d c" . docker-containers)
("C-c d i" . docker-images)
("C-c d v" . docker-volumes)
("C-c d n" . docker-networks))
Once configured, press C-c d to bring up the top-level Docker menu. From there, you can drill into containers, images, volumes, or networks.
The docker.el Transient Menu
The package uses transient (the same library powering Magit) to present contextual actions. When you open the Docker menu, you'll see something like:
Docker menu
------------
c Containers
i Images
v Volumes
n Networks
? Help
Selecting c for containers opens a tabulated list buffer showing all containers with columns for ID, image, status, ports, and names. From this buffer, you can mark containers and execute batch operations.
Working with Docker Images
To list all images, use M-x docker-images or press i from the Docker menu. This opens a dedicated buffer in tabulated-list-mode:
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 2b7c8c1e9f0 2 days ago 187MB
node 18-alpine 9a7b5c3d... 3 days ago 118MB
my-app v2 f4e8a9b2... 5 hours ago 245MB
From this buffer, you can perform various actions using single-key commands:
- Pull an image: Press
P(capital P) and enter the image name at the prompt - Delete an image: Mark images with
m, then pressDto delete marked - Run a container from an image: Press
ron the image line - Inspect an image: Press
RETto view detailed JSON inspection output - Tag an image: Press
tto add a new tag
Here is an example of pulling an image programmatically from your init file or via M-::
(docker-run "pull" "python:3.11-slim")
The image list buffer auto-refreshes based on docker-image-refresh-interval, so newly pulled images appear without manual reloading.
Managing Containers
The container list is where you'll spend most of your time. Open it with M-x docker-containers or c from the Docker menu. The buffer displays all containers (use C-c C-s to toggle between all and running-only):
ID IMAGE STATUS PORTS NAMES
a1b2c3d4 nginx Up 3 hours 0.0.0.0:8080->80/tcp web-server
e5f6g7h8 node:18 Exited (0) - build-step
i9j0k1l2 postgres:15 Up 2 days 0.0.0.0:5432->5432/tcp db
Key actions available in the container buffer:
- Start a stopped container:
S - Stop a running container:
O(capital o) - Restart:
R - Pause/Unpause:
P/U - Delete containers: Mark with
m, thenD - Open shell in container:
e(exec) — opens an interactive comint buffer with a shell inside the container - View logs:
l— opens a buffer tailing the container's logs - Inspect:
RET— full JSON inspection - Export container:
x— export container as tar archive
Attaching to a Container Shell
One of the most powerful features: press e on a running container, and Emacs prompts for the command to execute (defaults to /bin/bash or /bin/sh). It then opens a buffer in comint-mode — meaning you get full shell interaction with Emacs readline capabilities, history, and output searchability:
;; Equivalent programmatic invocation:
(docker-exec "web-server" "/bin/bash")
Inside this shell buffer, you can use C-c C-c to send signals, M-p/M-n for command history, and all standard comint navigation. The output is in a regular Emacs buffer, so you can copy, search, and save it.
Viewing Container Logs
Press l on a container to open a log buffer. This runs docker logs --follow in the background and streams output into Emacs. The buffer updates in near real-time. You can kill the log stream with C-c C-k (comint interrupt). For programmatic access:
(docker-logs "web-server" '("-f" "--tail=100"))
Docker Compose Integration
The docker-compose.el package extends the same transient-menu philosophy to Docker Compose stacks. Install it and bind a key:
(use-package docker-compose
:ensure t
:bind ("C-c C-d" . docker-compose-menu))
When you invoke docker-compose-menu, it scans for a docker-compose.yml or compose.yaml file in the current directory (or a parent directory) and presents available services:
Docker Compose menu (project: myapp)
-------------------------------------
Services:
web (node app, port 3000)
api (python fastapi, port 8000)
db (postgres 15)
redis (redis 7)
Actions:
u Up all services
d Down all services
r Restart services
l Logs
b Build
p Pull
You can operate on individual services or the entire stack:
- Bring up specific services: Mark services with
m, then pressu - View compose logs: Press
land select a service — logs stream into an Emacs buffer - Build images: Press
bto rebuild marked services - Run one-off commands: Press
eto execute an arbitrary command in a service container
For scripted compose operations, you can use the underlying functions directly:
;; Bring up the entire compose stack
(docker-compose-up)
;; Bring down and remove volumes
(docker-compose-down '("-v"))
;; Run a command in the web service
(docker-compose-exec "web" "npm test")
Working with Multiple Compose Files
If your project uses multiple compose files (e.g., docker-compose.yml and docker-compose.override.yml), docker-compose.el respects the standard Docker Compose file discovery order. You can also specify files explicitly:
(docker-compose-up "-f" "docker-compose.prod.yml")
Dockerfile Mode
dockerfile-mode provides syntax highlighting, indentation rules, and snippets for Dockerfiles. Install it and it automatically activates for files named Dockerfile or *.dockerfile:
(use-package dockerfile-mode
:ensure t
:mode (("Dockerfile\\'" . dockerfile-mode)
("\\.dockerfile\\'" . dockerfile-mode)))
Key features of dockerfile-mode:
- Syntax highlighting — recognizes all Dockerfile instructions:
FROM,RUN,COPY,CMD,EXPOSE,ENV,ARG,VOLUME,WORKDIR,USER,HEALTHCHECK,SHELL,ADD,ENTRYPOINT,LABEL,STOPSIGNAL,ONBUILD - Indentation awareness — continuation lines (using
\) are properly indented - Snippet expansion — with
yasnippet, you get templates for common patterns - Flycheck integration — if you have
hadolintinstalled, flycheck can lint your Dockerfile on save
Here's an example Dockerfile with proper highlighting in Emacs:
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
With dockerfile-mode active, each instruction gets distinct face colors, making multi-stage builds easy to parse visually.
Building from a Dockerfile
While editing a Dockerfile, you can build it directly. Use M-x docker-build from the Dockerfile buffer, or use the container/image menu. The build output appears in a compilation-mode buffer, so you can navigate errors with next-error (C-x \`):
;; Build with a specific tag from the current directory
(docker-build "." "-t" "myapp:latest")
You can bind a key for quick builds from Dockerfile buffers:
(add-hook 'dockerfile-mode-hook
(lambda ()
(local-set-key (kbd "C-c C-b")
(lambda ()
(interactive)
(docker-build "." "-t" "myapp:latest")))))
TRAMP Docker Integration — Editing Files Inside Containers
TRAMP (Transparent Remote Access, Multiple Protocol) is Emacs's built-in system for accessing remote files. Modern TRAMP includes a Docker method that lets you open files inside containers using a special path syntax:
/docker:container-name:/path/to/file
;; or with a specific user
/docker:user@container-name:/path/to/file
To open /etc/nginx/nginx.conf inside a running container named web-server:
C-x C-f /docker:web-server:/etc/nginx/nginx.conf RET
TRAMP transparently copies the file to a temporary local location, opens it in Emacs, and writes changes back to the container on save. This works with all Emacs modes — syntax highlighting, completion, linters, and even language servers function normally because the buffer sees a local file path while TRAMP handles the synchronization.
Browsing Container Filesystems with Dired
You can also browse a container's filesystem using Dired:
/docker:web-server:/var/log/
This opens a Dired buffer showing the container's /var/log/ directory. You can navigate, copy files between container and host, delete, rename, and change permissions — all through Dired's familiar interface.
Setting Up TRAMP for Docker
TRAMP's Docker support is usually available out of the box in Emacs 27+. To verify, check your TRAMP methods:
(require 'tramp)
;; List available methods — look for "docker"
(tramp-docker-method-p "docker")
If you need to add Docker explicitly to TRAMP methods:
(add-to-list 'tramp-methods
'("docker"
(tramp-login-program "docker")
(tramp-login-args (("exec") ("-it") ("%u") ("%h") ("/bin/sh")))
(tramp-copy-program "docker")
(tramp-copy-args (("cp") ("%u:%f") ("%k")))
(tramp-copy-from-args (("cp") ("%k") ("%u:%f")))))
For the docker-tramp.el package (which adds convenience wrappers):
(use-package docker-tramp
:ensure t
:config
(setq docker-tramp-use-names t)) ;; use container names, not IDs
TRAMP Docker Completion
Modern TRAMP offers completion for container names. When you type /docker: and press TAB, Emacs queries Docker for running containers and presents them as completion candidates. This makes navigating into containers as fast as navigating local files.
Building and Running Containers from Emacs
Beyond the interactive menus, docker.el provides functions for programmatic container operations. These are useful in custom commands, macros, or integration with project workflows.
Running a Container
;; Run an nginx container detached with port mapping
(docker-run "run" "-d" "--name" "web" "-p" "8080:80" "nginx:latest")
;; Run interactively with a TTY
(docker-run "run" "-it" "--rm" "alpine:latest" "/bin/sh")
Building an Image
;; Build with tags and build args
(docker-build "."
"-t" "myapp:latest"
"-t" "myapp:v2.0"
"--build-arg" "NODE_ENV=production")
Cleaning Up Resources
;; Remove all stopped containers
(docker-run "container" "prune" "-f")
;; Remove dangling images
(docker-run "image" "prune" "-f")
;; Full system prune
(docker-run "system" "prune" "-af")
Custom Docker Command Wrapper
You can wrap any docker command and capture its output in a buffer:
(defun my-docker-inspect-container (container-name)
"Inspect CONTAINER-NAME and display formatted output."
(interactive "sContainer name: ")
(let ((buf (get-buffer-create "*docker-inspect*")))
(with-current-buffer buf
(erase-buffer)
(insert (shell-command-to-string
(format "docker inspect %s" container-name)))
(json-mode)
(goto-char (point-min)))
(display-buffer buf)))
Best Practices for Emacs Docker Integration
1. Use Transient Menus as Your Primary Interface
The transient-based menus in docker.el and docker-compose.el are designed for discoverability. Rather than memorizing dozens of keybindings, learn the menu flow: C-c d → choose resource → single-key action. This reduces cognitive load and keeps you focused on the task.
2. Bind Docker Commands to Project-Specific Keys
If you work on a project with frequent Docker operations, create project-specific keybindings using projectile or project.el:
;; With projectile
(define-key projectile-mode-map (kbd "C-c p D")
(lambda () (interactive) (docker-compose-up)))
;; With directory-local variables
;; In .dir-locals.el:
((dockerfile-mode . ((eval . (local-set-key (kbd "C-c C-b")
(lambda ()
(interactive)
(docker-build "." "-t" "myapp")))))))
3. Integrate Log Viewing into Your Debugging Workflow
Container log buffers in Emacs are searchable, bookmarkable, and can be saved. When debugging, open logs with l from the container buffer, then use M-x occur to find error patterns, or M-x highlight-regexp to color-code important log lines. This is far more powerful than terminal-based log tailing.
4. Leverage TRAMP for Configuration Editing
Instead of exec-ing into a container to edit config files with vi/nano, use TRAMP: /docker:container:/etc/config.yml. You get full Emacs editing capabilities — multiple cursors, regex search/replace, undo tree, and all your custom modes. Save the buffer, and the container picks up changes immediately (or after a restart, depending on the service).
5. Combine Docker with Org Mode for Runbooks
For operational runbooks, embed Docker commands in Org mode code blocks:
#+begin_src sh :dir /ssh:prod-server|docker:web-container:/app
npm run migrate
#+end_src
This executes the migration command inside the production container, with the working directory set via TRAMP multi-hop syntax.
6. Set Reasonable Refresh Intervals
The live-updating container and image buffers can cause unnecessary Docker API calls if refresh intervals are too short. Set them based on your workflow:
(setq docker-container-refresh-interval 10) ;; seconds
(setq docker-image-refresh-interval 30)
(setq docker-volume-refresh-interval 60)
For low-resource environments, disable auto-refresh and use manual refresh (g in the buffer).
7. Use docker-compose.el for Multi-Service Workflows
When working with microservices, docker-compose.el shines. Bind it prominently and use the per-service log viewing. For development, map a key to restart a single service without bringing down the entire stack:
(defun restart-service (service)
(interactive "sService: ")
(docker-compose-restart service))
8. Keep an Emacs Dashboard Buffer
Create a dedicated frame or window configuration that shows containers, images, and compose services simultaneously. Emacs's window management lets you tile these buffers:
;; Example: split frame into containers + logs
(split-window-horizontally)
(docker-containers)
(split-window-vertically)
(docker-logs "web-server")
Save this window configuration with M-x window-configuration-to-register and restore it with C-x r j.
9. Handle Docker Socket Permissions Gracefully
If your Docker daemon requires sudo, configure TRAMP and docker.el to use it. For TRAMP:
(add-to-list 'tramp-methods
'("docker-sudo"
(tramp-login-program "sudo")
(tramp-login-args (("docker") ("exec") ("-it") ("%u") ("%h") ("/bin/sh")))
(tramp-copy-program "sudo")
(tramp-copy-args (("docker") ("cp") ("%u:%f") ("%k")))))
Then access containers via /docker-sudo:container:/path.
10. Version-Control Your Docker Configurations
With Emacs, your Dockerfiles, compose files, and related scripts all live in the same version-controlled project. Use Magit to track changes to Docker configurations alongside application code. This unified version control is a significant advantage over scattered terminal-based workflows.
Conclusion
Emacs Docker integration transforms the editor from a code-writing tool into a complete container orchestration environment. Through docker.el's transient menus, you gain rapid keyboard-driven management of images, containers, volumes, and networks. docker-compose.el extends this to multi-service stacks with the same intuitive interface. TRAMP's Docker method dissolves the boundary between host and container filesystems, letting you edit container files with full Emacs power. And dockerfile-mode ensures your Dockerfiles benefit from syntax highlighting, completion, and linting.
The key insight is that these tools compose together. You can browse a container's filesystem with Dired, open a configuration file with TRAMP, edit it with your fully customized Emacs, save it back to the container, rebuild the image from the Dockerfile buffer, restart the compose service, and tail the logs — all in a single Emacs session, without ever touching a separate terminal. For developers committed to the Emacs way, this integration is not merely convenient; it fundamentally elevates the Docker development experience by placing it inside the most powerful text manipulation environment ever created.