Ocaml-layer: Make your own base Docker images for fast OCaml CI

ocaml-layer (formerly “setup-ocaml”) is a small collection of scripts for creating custom Docker images and pushing them to Docker Hub. It solves the following problem:

  • Installing external dependencies such as native packages, the right version of the ocaml compilers, and public opam packages takes a while - typically over 5 minutes.
  • External dependencies don’t change very often, and we don’t we need nor want to build them each time some code changes in the project.
  • There are typically no pre-built Docker images available that already contain all the dependencies for a project.

Spinning up a container with all dependencies pre-installed takes about 1 min or less on a service like CircleCI. The rest of the CI job is spent entirely on building and testing the project’s code. This is what you’ll get.

The solution consists in specifying the list of native packages and a list of opam packages, building a Docker container with those, and pushing them to Docker Hub. While there’s nothing new about this approach, I believe it is now easier and still flexible.


Maybe some ideas from docker-slim could be helpful for you to make these images even leaner:

Here we’re concerned with providing a base image for building arbitrary software on top of it. Since it’s not a finished application, I don’t see how we could decide what to remove at this stage. I think the best we can do is start from a really small distribution (Alpine seems to fit the bill) and then install the packages we know we’ll need to build the application. After that, a lot of stuff can be discarded, for sure. One way I’m familiar with is exporting a statically-linked executable as an artifact. Multi-stage Docker builds make this very convenient. In this scenario, I’d have a Dockerfile that first builds my OCaml binary with -ccopt -static on Alpine on top of my rich base image, then copy it to the tiniest possible container where we’d run the static binary.

I should give this project a better name since it’s used by other projects out there and it’s not very descriptive or evocative anyway. There’s a brief window during which I’ll take suggestions, if you have any…

1 Like

I renamed the project from “setup-ocaml” to “ocaml-layer”. Another contender was “ocaml-blubber” but I thought it would have been too arcane.

1 Like

As always, excellent hacking Martin! You may find some of these utility functions handy to generate the Dockerfile:

(of course, your project is shell only and so probably doesn’t need to use an OCaml library for this)

One thing that we’ve found useful for reproducible and cacheable Dockerfiles is to specify revisions of opam-repo explictly rather than just opam init. If you specify an exact git checkout, it’ll reliably rebuild the Dockerfile when you update that hash.

To repeat what I said the last time you brought this up… [ANN] OCurrent 0.1 (CI/CD pipeline eDSL)


To pile onto this idea a bit, I just double-checked and this is possible to do from opam init. So if you want to initialize opam to use opam-repository from a point slightly back in time, just after the OCaml 4.11.0+beta2 packages were merged you can do this:

opam init https://github.com/ocaml/opam-repository.git#82b8810e668724278389680b1a38bc3ff35050e9