This repo is a minimal OCaml project that demonstrates a cacheable Docker layer for Dune Package Management. It is intentionally tiny, but still builds the OCaml compiler and packages so you can see the Docker layer cache savings.
Blog post: https://makerprism.com/en/blog/a-practical-docker-cache-pattern-for-dune-package-management/
This is how I personally handle it. It is not a recommendation from the Dune team. I would love feedback or to hear how others handle this.
Dockerfile.unoptimized: copies the full repo first, so any source change invalidates the layer that builds the OCaml compiler and all dependencies.Dockerfile.optimized: copies onlydune-project, runsdune pkg lock, and runsdune build @pkg-install, so the expensive compiler + dependency layer is cached. The real source code is copied afterward.
Cold builds:
time docker build -f Dockerfile.unoptimized .
time docker build -f Dockerfile.optimized .Warm builds (after editing src/site.ml):
printf '%s\n' 'let () = print_endline "Hello again"' > src/site.ml
time docker build -f Dockerfile.unoptimized .
time docker build -f Dockerfile.optimized .Architecture:
- OS: Linux pop-os 6.17.9-76061709-generic
- CPU: AMD Ryzen 7 7840HS (16 threads)
- Arch: x86_64
Timings (local Docker, BuildKit enabled):
- Average warm rebuild (2 runs with actual source changes): unoptimized 109.7s, optimized 2.2s
- The cache boundary is the
dune pkg lock+dune build @pkg-installlayer. dune.lockis generated during the Docker build and is not checked in.- This is Docker layer caching, not the Dune cache.