Edit files in (remote) Docker containers

January 12, 2021

I always had problems remembering Docker commands: to run a shell in a container, should I use docker run or docker exec? Wait, I probably need to "attach" the terminal to the running container. No problem, let's try docker attach. Ooops...

These commands initially made no much sense to me - run and exec sounds like an alias to me and attach; well, Docker authors clearly had the idea that attaching to container and attaching shell input (typing commands inside container shell) are entirely unrelated things.

Because of this, I kept personal docker.org file with all the notes how I'm going to start a container, run something in it, and so on.

Here is another problem. Most containers are built to be minimal, providing only necessary stuff for containerized application. So, I'd frequently find things like a complete JDK stack, tons of python libraries, or many nodejs packages.

But not a single editor. No vim, emacs, nano... not even "the standard editor" ed ;) You'll get cat, sometimes less, but try to modify something with those.

I'm aware that container purists will scream at me that I should not edit anything inside containers, but let say that theory and edge cases from real life are on the complete opposite side. Not once I had to hack things around, inside a container, without shutting it down the application.

So, Emacs to the rescue, as usual. Thanks to awesome docker-tramp mode, Emacs can access to Docker containers trough TRAMP and bring all the goodies that comes with it: dired, eshell, shell, file syntax highlighting and, yes, editing.

Download it, load it, and let see what it can do.

For example, to access a locally running container, with CONTAINER_ID id:

C-x C-f /docker:CONTAINER_ID:/

evil-mode users will use:

:e /docker:CONTAINER_ID:/

And dired will be running inside container. eshell and shell will work as well.

There is more. You can access to remote container via multiple hops with the following:

C-x C-f /ssh:remote-server-ip|docker:CONTAINER_ID:/ 

Image will help:

docker-multiple-hops1.png

You can also have a more complicated setup, like jumping through bastion host(s):

C-x C-f /ssh:remote-server-ip1|ssh:remote-server-ip2|docker:CONTAINER_ID:/ 

docker-multiple-hops2.png

There is even more!

Let say you want to evaluate (or run) python code inside that remote Docker container, assuming the container has installed python? No problem at all. Do this, as explained here:

C-x C-f /ssh:remote-server-ip|docker:CONTAINER_ID:/ 
M-x run-python

And you'll get a python shell inside your Emacs. Open some local python file and start evaluating python expressions - they will be executed in a remote container.

Notice that docker-tramp has a couple of caveats:

  1. It expects the container to be up and running and will not start it.
  2. It doesn't have auto-completion of container ids unless you apply this patch.

However, for me, it is good enough :)