Introduction to opam for npm/yarn users

Getting Started

opam is the main package manager for OCaml. This is the equivalent of npm or yarn. opam is a command line tool to manipulate packages that are defined in opam files. Most of the opam packages are published on the opam repository which is the equivalent of the npm registry.

This document presents the corresponding opam commands, files and configurations for the most common npm idioms.

opam is now available on version 2. If you didn't upgrade yet, you better hurry.

There is a summary at the end of the page, containing all the most common commands to know. And the the official opam introduction contains a lot of information: https://opam.ocaml.org/doc/2.0/Usage.html

Initial configuration


First thing is to install opam, as one would install npm. There is an official documentation page to install opam. Most of the time, we can simply get it from a package manager. Otherwise, binaries are provided for every platform.


npm and yarn don't need any initialization after they are installed, even if it is possible to customize a few settings. But for opam there is a necessary step.

opam init -a

Let's try to explain more in details what it does.

I am quoting the documentation of opam init itself here, as it explains clearly what is done.

The init command initialises a local "opam root" (by default, ~/.opam/) that holds opam's data and packages. This is a necessary step for normal operation of opam. The initial software repositories are fetched, and an initial 'switch' can also be installed, according to the configuration and options. These can be afterwards configured using opam switch and opam repository.


Additionally, this command allows to customise some aspects of opam's shell integration, when run initially (avoiding the interactive dialog), but also at any later time.

The interesting parts are:

  • the opam root is ~/.opam
  • opam uses a shell integration to make our life easier
  • opam uses the concept of switch

A switch is the equivalent of the node_modules folder in npm's world. It contains all the packages that are installed. One difference with npm is that we can have multiple global switches. As if we could have different yarn global projects. It can be handy sometimes, but to avoid confusion I recommend to avoid using global switches.

Default settings can be changed if the -a option is omitted while calling opam init.

Minimal package.json

The equivalent of package.json is a app.opam file. The part before .opam is the name of the package. So app.opam could be webpack.opam. It is possible to have multiple opam files in the same directory.

There is no opam command to manipulate the opam file. Things like npm init or yarn add will have to be done by hand.

A minimal opam file looks like this:

opam-version: "2.0"
name: "my-app"
authors: "Louis"
homepage: "https://github.com/khady/example"
maintainer: "Louis <ex@ample.com>"
dev-repo: "git+ssh://git@github.com:khady/example.git"
bug-reports: "https://github.com/khady/example/issues"
version: "0.1"
build: [
  [ "dune" "subst" ] {pinned}
  [ "dune" "build" "-p" name "-j" jobs ]
depends: [
  "dune" {build}
  "opam-lock" {dev}

{build} tells opam that dune is needed only to build to project. {dev} is to mark dev dependencies. There is another way to handle dev dependencies for advance usage that I covered in this post.

npm/yarn commands

npm install / yarn

npm install or yarn commands cover multiple opam commands, depending on the context.

First case is in absence of local switch in the current project. It can be verified by looking for a _opam directory at the root of the project. It corresponds to a npm project without a node_modules directory.

In this first case, we need to initialize a local switch.

opam switch create . 4.07.0 --deps-only

The second case is in presence of a local switch.

opam install . --deps-only

npm install <pkg> / yarn add

To install a package with opam is easy:

opam install PACKAGE

But opam does not modify the app.opam file during the installation. It has to be done by hand. This is as simple as adding the name of the package in the depends field.

npm link / yarn link

npm link in the opam world is opam pin. Its usage is well described in the official documentation. You can find it there:


npm upgrade / yarn upgrade

In this case, there is a direct equivalent in opam. And it is easy to remember.

opam upgrade PACKAGE

opam upgrade is also able to upgrade all the packages of the local switch if no package name is given.

There is one big difference with npm though. opam stores a local copy of the opam repository. Like apt-get does in Debian. So if we often want to update this copy before to request an upgrade.

opam update && opam upgrade PACKAGE

Extra package.json concepts

dev dependencies

There is no strict equivalent of the dev-dependencies field of package.json. But it is possible to achieve the same thing in two ways.

  • By marking with {dev} the package in depends
  • By using a dev.opam file, as I described there.

lock files

Lock files are also not common yet in the opam world. But I talked about their usage on this page. It can be summarize to:

  • Using opam lock to generate the lock file when needed (basically after each opam install or opam upgrade).
  • Adding --locked to all the opam install . --deps-only and opam switch create . commands.


This one doesn't exist in the opam world. The closer we get is the equivalent of yarn exec. Which is opam exec.

Instead of writing a package.json with a script field like this:

"scripts": {
  "release": "make release"

We run opam exec like this:

opam exec -- make release


In javascript packages published on npm, the convention is to have one library per package. This is not the case in opam. One opam package can provide multiple libraries. And there is a tool called ocamlfind that is used to get the information related to all those libraries. When using a modern build system like dune, there will be almost no reason to use ocamlfind by hand.

There is no official way that I know off to get the list of all the libraries installed by a package. The easiest way to get a good approximation is something like this:

cat $(opam var re:lib)/META | grep package

Which tells you than the package re comes with the libraries re, re.emacs, re.glob, re.pcre, re.perl, re.posix and re.str.




Starting a new project

This section does the same as: yarn init.

Create app.opam file with this content:

opam-version: "2.0"
name: "my-app"
authors: "Louis"
homepage: "https://github.com/khady/example"
maintainer: "ex@ample.com"
dev-repo: "git+ssh://git@github.com:khady/example.git"
bug-reports: "https://github.com/khady/example/issues"
version: "0.1"
build: [
  [ "dune" "subst" ] {pinned}
  [ "dune" "build" "-p" name "-j" jobs ]
depends: [
  "dune" {build}
  "opam-lock" {dev}
opam switch create . 4.07.0 --deps-only

Updating the list of packages available

Opam is like apt-get, it requires the execution of a command to get the list of new packages available.

opam update

Adding a dependency

This section does the same as: yarn add.

  • Add a line in the depends field of app.opam:
depends: [
  "dune" {build}
  • Install the new dependency:
opam install . --deps-only

Upgrading a dependency

This section does the same as: yarn upgrade.

opam upgrade [package]

Removing a dependency

This section does the same as: yarn remove.

  • Execute opam remove to uninstall the package:
opam remove owl
  • Remove the corresponding line from the depends field in app.opam

Installing all the dependencies of project

This section does the same as: yarn install.

If there is a _opam folder (a local switch) in the project:

opam install . --deps-only


opam switch create . 4.07.0 --deps-only
Louis Roché 2018-08-30 Thu 00:00 Emacs 25.1.1 (Org mode 9.2.1)