When you mess around with new computer stuff as much as I do, you're going to make a mess after a while. Old, discarded and unused tools from a menagerie of half-baked experiments litter my laptop, their libraries scattered among uncountable filesystem subtrees, their mothballed compilers gathering dust. A few tidy rooms where doomed projects were disposed of in a computing-environmentally friendly manner mark the exceptions which prove the rule. Building the projects which remain is an exercise in archaeology.

But it doesn't have to be this way.

Other developers have been putting development environments in Docker for years now, and I only recently joined the train. The idea is rather simple: take all of the mess that comes with a given programming setup and package it all up in one place, isolated from the rest of your computer. At any time you can throw it all away with no mess. You can relocate that environment to other computers and replicate it exactly, without having to remember the magic incantations. When you pick up an old project, you can build it—cleanly and reliably.

Of course, the devil is in the details: how do you combine toolsets for mixed-language projects? How do you interact with the tools from the host machine? What about editor integrations? I've been wrestling with these problems recently as I set up a sandbox for trying out ReasonML. My solution involves three layers.

The first is an image called ndpi/emacs-base: it pulls in Emacs, my text editor of choice, and combines it with various tools (Git, curl, and so on) and the configuration files required to run it with my customizations. Running the resulting image executes Emacs in the sandbox, giving me a basic portable editor setup.

Building on that foundation, I add tmux and set up a new user in the sudoers group to create a development base image (aptly, ndpi/dev-base). This image starts tmux instead of emacs, and has a hook set up so that child images can contribute a shell script named which will initialize one pane of a split view created on image startup.

Now the stage is set: new programming environments can be created easily by taking the ndpi/dev-base image and extending it with whatever tools are required. Servers or compilers can be started with scripts, and a hand-rolled Emacs module system lets me program the editor on the fly with new packages for the specific language I'm using.

Containers give me a clean, repeatable setup every time along with a document of the decisions I make over time via the Dockerfile. I can dispose of unneeded tools easily and cleanly, and port my environments to new machines with minimal fuss. Depending on my mood source code can be mounted into the container using Docker bind mounts or I can work in a throwaway sandbox environment. My development environment is now organized in such a way that I can garden it, instead of leaving it to grow wild and weed-choked over time.

A Gist with the major files is here, for the curious.