Fork me on GitHub

Copy-pasting some recent important changes to the official clojure Docker images. These were originally posted in the clojure Google Group so if you already read them over there, nothing new here. 🧵


JDK 17 is the new LTS release, and as a result, is now the JDK version you get if you don't specify a different one in your Docker tag (which we recommend doing in production).


The clojure:latest image (which is what you get if you only specify 'clojure' for your image name) now defaults to clj (i.e. tools.deps) instead of lein. When these images were first created, Leiningen was the de facto Clojure build tool. Nowadays we have an official first party tool, so we've moved to that. As always, we only recommend using this image in one-off commands and specifying build tool, clojure version, Linux distro, and openjdk base version where those matter.


Previously any image that didn't mention a build tool in its Docker tag (e.g. clojure:openjdk-11) would have lein installed (so clojure:openjdk-11 was the same as clojure:openjdk-11-lein). Now, however, those images have also been switched to clj. So now clojure:openjdk-11 has clj as its only build tool (remember that only the clojure / clojure:latest image has all three build tools installed). clojure:openjdk-11 is now the same as clojure:openjdk-11-tools-deps.


All images using openjdk-17 or higher now use an ENTRYPOINT of their respective build tool (i.e. lein, clj, or boot) and a CMD that launches a REPL instead of the previous practice of the CMD being build-tool repl (roughly). This means a bare docker run should behave approximately the same way as before, except you can now do things like: docker run clojure -e '(+ 1 2)' and it will print '3'. We left the older openjdk images alone in case folks were relying on the old behavior in some cases, and we don't want to break things where people were using full-specification tags.   - If you're not familiar with this ENTRYPOINT vs. CMD concept in Docker, it basically boils down to this: The CMD gets replaced with whatever you put after the image name in a docker run command. So now you can specify arguments to the build tool of whatever image you're running instead of redundantly specifying it first. You can override the default ENTRYPOINT with the --entrypoint foo arg to docker run (must come before the image name) but see the "important caveat" below as many use cases will hopefully be covered already without using that.   - An important caveat: The entrypoint script we're using tries to detect if you specify a CMD that isn't an argument nor subcommand of that image's build tool and runs it as a standalone executable. In other words, docker run -ti clojure bash still gets you a bash prompt. Please report issues on if you find errors in that logic. It is a requirement that official Docker images behave this way, but we also want to make sure it is robust in the vast majority of use cases.


Some time ago we announced that debian slim-bullseye would be the default distro you'd get if you didn't specify one, but not all of our images were migrated to that default. Now they all are hopefully.


The alpine-based images (only available for openjdk-18 currently) are now using Alpine Linux 3.15 which was recently released. The biggest change you'd probably notice there is that an rlwrap package is finally available in Alpine and installed in the tools-deps images so the clj repl will behave as it should.


There is a long-standing bug in Docker that makes rlwrap crash if it is started too soon after a container starts. We used to work around this with a naive 1 second wait in the tools-deps images. We are now shipping with a much better solution that waits a much shorter amount of time when it can, but still waits for as long as it needs to on slower systems. This means you should generally notice faster container starts for tools-deps images than before.


Clarification: The clojure / clojure:latest image still includes all three build tools (lein, boot, tools-deps). Only the default one you get if you don't specify a different one has changed from lein to clj. But this image has also inherited the ENTRYPOINT / CMD changes I outlined above, so be aware of that. It should figure out what you want if you do something like docker run clojure lein though, so let us know on GitHub ( if it doesn't do what you expect in this regard.


This is (yet another) good moment to remind everyone to use fully specified Docker tags as much as possible (e.g. clojure:openjdk-17-tools-deps- rather than relying on the defaults (as they can and do change from time to time). A good heuristic might be: Typing a Docker tag into a shell command? Fine to rely on defaults or just run clojure / clojure:latest. Typing a Docker tag into a code editor? Use a fully-specified tag.


It depends off course. Using latest might also prevent security risks. But there is also the risk of not really knowing what will spin up.


I would strongly recommend against using latest as your security risk avoidance strategy :)