Should opam use a lockfile by default?

I think this comment is underrated and deserve a separate thread. from an Enterprise user’s point of view I can’t emphasize enough how important it is to guarantee consistent and reproducible build. It’s no coincidence that almost all other modern package managers support this. dune can potentially help if someone can figure out a good vendoring workflow (similar to Go), but as it stands the story is not great.

11 Likes should help here. It generates lock file for reproducible build.

1 Like

I’ve moved this into its own topic.

Changing defaults of existing tools is always very hard work. What I haven’t seen is any real reports of people using the opam 2.x opam-lock mechanism. Have you tried this?

From the dune side, we’re building a hybrid opam/dune vendoring system called duniverse which is showing very useful results on some of our larger projects (e.g. Real World OCaml v2 is fully vendored and only needs dune to build).


I always use opam together with opam-lock in my personal OCaml projects, and it’s working well.

Esy, at the moment, sits in an uncanny valley for me though. On one hand, it’s serving us well in a Reason project at work. User experience is friendlier to people with different backgrounds and skill-sets, and devs seem to enjoy using libs which are not available on opam. On my side, it definitely decreased the amount of support I need to provide compared to opam. Especially around making sure everyone’s development environment is up to date, since Esy does not allow pulling latest package.json and lockfile changes without updating the dependency tree.

On the other hand, I’m hesitant to adopt Esy for all my OCaml projects if I’m not constrained by the requirements I listed above. Don’t get me wrong, it does a great job at cross-platform support and even packaging C libraries, but I still can’t help but feel like Nix will serve me better when I’m more concerned about reproducibility and less about user experience of the tool.

That said, duniverse looks very interesting, especially for projects that don’t have heavy dependencies on system libraries! I’d love to hear more about it. Do you think it is already a good fit, compared to opam lockfiles and Esy, for ensuring long term health of projects that are basically “done” and don’t require much maintenance?


This looks very promising! I’m going to give opam-lock a try. I also look forward to the public announcement of duniverse.

1 Like

I would wager almost no one knows about it :slight_smile: it’s not documented at

The most detailed online documentation I found on opam lock is at

If we push people to use it we will hear reports, I think.


Fair point – it’s pretty hidden away, but there’s instructions as part of the Reproducible Builds tips over at: as well.


At Ahrefs we use lockfiles for applications development and they are pretty useful. We are still missing a convenient way to run opam commands while making sure that a lock opam (or even a normal opam file) is respected. As what is done by esy/yarn. I’m thinking of writing an opam plug-in to do it. But it would be better if it was part of opam.

I wrote some documentation about opam locks mostly for myself but maybe it can help.


Some notes:

  • Here is a long Medium post from a developer involved in the Go packaging system, which surveys some aspects of package management. I don’t particularly recommend reading it unless you are very much into package management, but it has a section about Lockfiles that might be interesting to people involved in this discussion:

  • I am personally a Nix user, so I rarely use opam, and I have never managed yet to try esy. I had a pretty bad experience trying to package esy in nixpkgs: (unfinished draft PR, help welcome). I discovered during the process that esy depends on very old (I’d dare say obsolete) libraries.

  • I have been aware of duniverse for a long while but I have never tried it due to the lack of public documentation. And yet, I would be very interested to test this tool.

Is there a way to install ocaml via the opam package dependency rather than the local switch?

I haven’t tried it yet, but check out this announcement:

edit: nvm, looks like it patches the release binaries

1 Like

Are you talking about dose3? I thought opam also uses it.

Right. I was very surprised to discover that this package was not updated since 2016. Instead of getting compatibility updates for new versions of OCaml, it gets patched directly in the opam repository ( Looking at the Debian package, it seems to be patched even more (

It seems that you are correct and opam still relies on it (although it has some make magic to fetch external dependencies and build them during the opam build process). I personally don’t get how people can live with such a situation when it could have been adopted and maintained within the opam source repository instead, and new versions published from there. Having all package maintainers figure out what patches they need and copying them around seems very stupid and unhealthy to me.

Just chiming in to remark that dose3 is also a pain point on flambda switches (and, I think, spacetime switches). Namely, it somehow requires a ginormous amount of RAM to be compiled.

1 Like

Just wanted to add that infer uses lock files as one more datapoint.

Indeed, anyone who is serious about consistently building their package in the face of a changing package database will use lock files. I also want to point out that opam-lock does not guarantee full reproducibility. AFAIK, if a package is deleted out of opam-repository, then an opam-lock will no longer be able to refer to it (This seems to happen often enough). The only way to guarantee reproducibility with opam right now is have lock file and snapshot the metadata in the repository.


And there is opam switch export --full to have the metadata. But it is not as convenient to use.

I would like to mention that the maintainers of opam are very reactive and trying to resolve the problems that are raised to them. All the discussions around lock files lead to quick changes to deliver what is the current version. But from what @AltGr told me during the ICFP, it seems that they have little feedback on who are using the lockfiles, if it is succesfull or not, and what is not working.

There are plenty of good ideas of esy that are being implemented into opam too… when someone ask the opam maintainers to do it :slight_smile:

For example, there is which allows in one command to install a package and add it to an opam file. Unfortunately, it doesn’t update the lock file automatically yet. Probably because no one asked for this feature.

There is also a new option --check to opam install. It checks that dependencies of given packages are satisfied in a given switch.

It’s probably not difficult now to write a small plugin opam x CMD which would do:

  • search for an opam file in the current dir or a parent
  • if it finds one, run opam install --check path/to/opam/file
  • if the check fails, report that opam install path/to/opam/file --deps-only must be executed first
  • run opam exec CMD

Things are not perfect, but we are slowly getting there


The lock files help a lot for reproducible builds, but there are still failure modes. For example, even after switching infer to use a lock file, it happened that an existing package was updated to change its dependency information without changing the package version, and the result was build failure.

So there is an argument that both a lock file and an overall shift to packages being immutable are needed. (For reproducible internal builds we tarball the repo and package sources.)


I didn’t realize this wasn’t the case already. Not sure how one can have a reproducible builds without an immutable package registry. Perhapas the notion of a package version could adopt a git commit hash since opam-repository is already in a git repo.

It has happened unfortunately, but we are now trying to avoid mutating the published packages as much as possible unless one of the following reasons applies:

  • the source tarball cannot be found anywhere any longer and has been modified upstream
  • there is some very serious bug (in that case the package will become unavailable)
  • the dependencies are wrong (missing bounds or dependencies, change in transitive dependencies): in such case we just fix the dependencies (and test it against all compilers versions)
  • new depexts for new systems are introduced

I cannot think of other reasons at the moment. When adding patches we are trying now to add -k to the version to increase it but make clear that there have been changes. So I agree that this has been a problem, and I have bee guilty of it myself, but we are getting better at it. And it will improve even mire once the builds become stricter on transitive dependencies imo.