diff --git a/gnu/packages/ocaml.scm b/gnu/packages/ocaml.scm
index 289b86cc27..d96af0ee2d 100644
--- a/gnu/packages/ocaml.scm
+++ b/gnu/packages/ocaml.scm
@@ -833,7 +833,225 @@ (define-public ocaml-opam-file-format
     ;; With static-linking exception
     (license license:lgpl2.1+)))
 
+(define-public ocaml-opam-core
+  (package
+    (name "ocaml-opam-core")
+    (version "2.1.5")
+    (source (origin
+              (method git-fetch)
+              (uri (git-reference
+                    (url "https://github.com/ocaml/opam")
+                    (commit version)))
+              (file-name (git-file-name name version))
+              (sha256
+               (base32
+                "0ckd87rcmcz11iyhhm5qnmy27jbdffx6n1fr06hvrqqrzi00jljh"))))
+    (build-system dune-build-system)
+    (native-inputs (list ocaml-cppo))
+    (propagated-inputs (list ocaml-graph ocaml-re))
+    (arguments `(#:package "opam-core"
+                 ;; tests require the opam-client library that is not yet available
+                 #:tests? #f))
+    (home-page "https://opam.ocaml.org/")
+    (synopsis "Package manager for OCaml")
+    (description
+     "OPAM is a tool to manage OCaml packages.  It supports multiple
+simultaneous compiler installations, flexible package constraints, and a
+Git-friendly development workflow.")
+    ;; The 'LICENSE' file waives some requirements compared to LGPLv3.
+    (license license:lgpl3)))
+
+(define-public ocaml-opam-format
+  (package
+    (inherit ocaml-opam-core)
+    (name "ocaml-opam-format")
+    (native-inputs '())
+    (propagated-inputs (list ocaml-opam-core
+                             ocaml-opam-file-format
+                             ocaml-re))
+    (arguments `(#:package "opam-format"
+                 ;; tests require the opam-client library that is not yet available
+                 #:tests? #f))))
+
+(define-public ocaml-opam-repository
+  (package
+    (inherit ocaml-opam-core)
+    (name "ocaml-opam-repository")
+    (native-inputs '())
+    (propagated-inputs (list ocaml-opam-format))
+    (arguments `(#:package "opam-repository"
+                 ;; tests require the opam-client library that is not yet available
+                 #:tests? #f))))
+
+(define-public ocaml-opam-state
+  (package
+    (inherit ocaml-opam-core)
+    (name "ocaml-opam-state")
+    (native-inputs '())
+    (propagated-inputs (list ocaml-opam-repository))
+    (arguments `(#:package "opam-state"
+                 ;; tests require the opam-client library that is not yet available
+                 #:tests? #f))))
+
+(define-public ocaml-opam-solver
+  (package
+    (inherit ocaml-opam-core)
+    (name "ocaml-opam-solver")
+    (native-inputs '())
+    (propagated-inputs (list ocaml-opam-format
+                             ocaml-mccs
+                             ocaml-dose3))
+    (arguments `(#:package "opam-solver"
+                 ;; tests require the opam-client library that is not yet available
+                 #:tests? #f))))
+
+(define-public ocaml-opam-client
+  (package
+    (inherit ocaml-opam-core)
+    (name "ocaml-opam-client")
+    (native-inputs '())
+    (propagated-inputs '())
+    (inputs (list ocaml-opam-state
+                  ocaml-opam-solver
+                  ocaml-base64-boot
+                  ocaml-opam-repository
+                  ocaml-re
+                  ocaml-cmdliner))
+    (arguments `(#:package "opam-client"
+                 ;; TODO
+                 #:tests? #f))))
+
 (define-public opam
+  (package
+    (inherit ocaml-opam-core)
+    (name "opam")
+    (arguments
+     `(#:package "opam"
+       #:phases
+       (modify-phases %standard-phases
+         (add-before 'build 'pre-build
+           (lambda* (#:key inputs make-flags #:allow-other-keys)
+             (let ((bash (assoc-ref inputs "bash"))
+                   (bwrap (search-input-file inputs "/bin/bwrap")))
+               (substitute* "src/core/opamSystem.ml"
+                 (("\"/bin/sh\"")
+                  (string-append "\"" bash "/bin/sh\""))
+                 (("getconf")
+                  (which "getconf")))
+               ;; Use bwrap from the store directly.
+               (substitute* "src/state/shellscripts/bwrap.sh"
+                 (("-v bwrap") (string-append "-v " bwrap))
+                 (("exec bwrap") (string-append "exec " bwrap))
+                 ;; Mount /gnu and /run/current-system in the
+                 ;; isolated environment when building with opam.
+                 ;; This is necessary for packages to find external
+                 ;; dependencies, such as a C compiler, make, etc...
+                 (("^add_sys_mounts /usr")
+                  (string-append "add_sys_mounts "
+                                 (%store-directory)
+                                 " /run/current-system /usr")))
+               (substitute* "src/client/opamInitDefaults.ml"
+                 (("\"bwrap\"") (string-append "\"" bwrap "\""))))))
+         (add-before 'check 'prepare-checks
+           (lambda* (#:key inputs #:allow-other-keys)
+             ;; Opam tests need to run an isolated environment from a writable
+             ;; home directory.
+             (mkdir-p "test-home")
+             (setenv "HOME" (string-append (getcwd) "/test-home"))
+             (with-output-to-file (string-append (getcwd) "/test-home/.gitconfig")
+               (lambda _
+                 (display "[user]
+email = guix@localhost.none
+name = Guix Builder")
+                 (newline)))
+
+             ;; Opam tests require data from opam-repository. Instead of
+             ;; downloading them with wget from the guix environment, copy the
+             ;; content to the expected directory.
+             (substitute* "tests/reftests/dune.inc"
+               (("tar -C.*opam-archive-([0-9a-f]*)[^)]*" _ commit)
+                (string-append "rmdir %{targets}) (run cp -r "
+                               (assoc-ref inputs (string-append "opam-repo-" commit))
+                               "/ %{targets}) (run chmod +w -R %{targets}"))
+               (("wget[^)]*") "touch %{targets}")
+               ;; Disable a failing test because it tries to clone a git
+               ;; repository from inside bwrap
+               (("diff upgrade-format.test upgrade-format.out") "run true")
+               ;; Disable a failing test because it tries to figure out which
+               ;; distro this is, and it doesn't know Guix
+               (("diff pin.unix.test pin.unix.out") "run true")
+               ;; Disable a failing test because of a failed expansion
+               (("diff opamroot-versions.test opamroot-versions.out") "run true")
+               ;; Disable a failing test, probably because the repository we
+               ;; replaced is not as expected
+               (("diff opamrt-big-upgrade.test opamrt-big-upgrade.out") "run true"))
+             (substitute* "tests/reftests/dune"
+               ;; Because of our changes to the previous file, we cannot check
+               ;; it can be regenerated
+               (("diff dune.inc dune.inc.gen") "run true"))
+             ;; Ensure we can run the generated build.sh (no /bin/sh)
+             (substitute* '("tests/reftests/legacy-local.test"
+                            "tests/reftests/legacy-git.test")
+               (("#! ?/bin/sh")
+                (string-append "#!"
+                               (search-input-file inputs "/bin/sh"))))
+             (substitute* "tests/reftests/testing-env"
+               (("OPAMSTRICT=1")
+                (string-append "OPAMSTRICT=1\nLIBRARY_PATH="
+                               (assoc-ref inputs "libc") "/lib"))))))))
+    (native-inputs
+     (let ((opam-repo (lambda (commit hash)
+                        (origin
+                          (method git-fetch)
+                          (uri (git-reference
+                                (url "https://github.com/ocaml/opam-repository")
+                                (commit commit)))
+                          (file-name (git-file-name "opam-repo" commit))
+                          (sha256 (base32 hash))))))
+       `(;; For tests.
+         ("bubblewrap" ,bubblewrap)
+         ("git" ,git-minimal/pinned)
+         ("openssl" ,openssl)
+         ("python" ,python-wrapper)
+         ("rsync" ,rsync)
+         ("unzip" ,unzip)
+         ("which" ,which)
+
+         ("opam-repo-0070613707" ,(opam-repo "00706137074d536d2019d2d222fbe1bea929deda"
+                                           "1gv1vvmfscj7wirfv6qncp8pf81wygnpzjwd0lyqcxm7g8r8lb4w"))
+         ("opam-repo-009e00fa" ,(opam-repo "009e00fa86300d11c311309a2544e5c6c3eb8de2"
+                                           "1wwy0rwrsjf4q10j1rh1dazk32fbzhzy6f7zl6qmndidx9b1bq7w"))
+         ("opam-repo-7090735c" ,(opam-repo "7090735c9d1dd2dc481c4128c5ef4d3667238f15"
+                                           "1bccsgjhlp64lmvfjfn6viywf3x73ji75myg9ssf1ij1fkmabn0z"))
+         ("opam-repo-a5d7cdc0" ,(opam-repo "a5d7cdc0c91452b0aef4fa71c331ee5237f6dddd"
+                                           "0z7kawqisy07088p5xjxwpvmvzlbj1d9cgdipsj90yx7nc5qh369"))
+         ("opam-repo-ad4dd344" ,(opam-repo "ad4dd344fe5cd1cab49ced49d6758a9844549fb4"
+                                           "1a1qj47kj8xjdnc4zc50ijrix1kym1n7k20n3viki80a7518baw8"))
+         ("opam-repo-c1842d168d" ,(opam-repo "c1842d168de956caf06d7ac8588e65020d7594d8"
+                                             "142y1ac7sprygyh91shcp0zcyfxjjkshi9g44qgg4rx60rbsbhai"))
+         ("opam-repo-c1d23f0e" ,(opam-repo "c1d23f0e17ec83a036ebfbad1c78311b898a2ca0"
+                                           "0j9abisx3ifzm66ci3p45mngmz4f0fx7yd9jjxrz3f8w5jffc9ii"))
+         ("opam-repo-f372039d" ,(opam-repo "f372039db86a970ef3e662adbfe0d4f5cd980701"
+                                           "0ld7fcry6ss6fmrpswvr6bikgx299w97h0gwrjjh7kd7rydsjdws"))
+         ("opam-repo-11ea1cb" ,(opam-repo "11ea1cb6f2418b1f8a6679e4422771a04c9c3655"
+                                          "1s4p0wfn3bx97yvm8xvj3yhzv2pz0jwml68g2ybv37hj9mpbrsq0"))
+         ("opam-repo-297366c" ,(opam-repo "297366cd01c3aaf29b967bf0b34ccc7989d4d5b3"
+                                          "1ysg69gys37nc2cxivs2ikh6xp0gj85if4rcrr874mqb9z12dm0j"))
+         ("opam-repo-3235916" ,(opam-repo "3235916a162a59d7c82dac3fe24214975d48f1aa"
+                                          "1yf73rv2n740a4s9g7a9k4j91b4k7al88nwnw9cdw0k2ncbmr486"))
+         ("opam-repo-de897adf36c4230dfea812f40c98223b31c4521a"
+          ,(opam-repo "de897adf36c4230dfea812f40c98223b31c4521a"
+                      "1m18x9gcwnbar8yv9sbfz8a3qpw412fp9cf4d6fb7syn0p0h96jw")))))
+    (propagated-inputs '())
+    (inputs (list ocaml-opam-client
+                  ocaml-opam-state
+                  ocaml-opam-solver
+                  ocaml-base64-boot
+                  ocaml-opam-repository
+                  ocaml-re
+                  ocaml-cmdliner))))
+
+(define-public opam-2.1.3
   (package
     (name "opam")
     (version "2.1.3")
@@ -941,6 +1159,8 @@ (define-public opam
          ("which" ,which)
 
          ;; Data for tests
+         ("opam-repo-0070613707" ,(opam-repo "00706137074d536d2019d2d222fbe1bea929deda"
+                                           "1wwy0rwrsjf4q10j1rh1dbzk32fbzhzy5f7zl6qmndidx9b1bq7w"))
          ("opam-repo-009e00fa" ,(opam-repo "009e00fa86300d11c311309a2544e5c6c3eb8de2"
                                            "1wwy0rwrsjf4q10j1rh1dazk32fbzhzy6f7zl6qmndidx9b1bq7w"))
          ("opam-repo-7090735c" ,(opam-repo "7090735c9d1dd2dc481c4128c5ef4d3667238f15"
