diff --git a/release.nix b/release.nix
index 29c0bb0d229f21418129541dffe0e7fa6c10c619..95fc90024a8b5e21c520ae2677497782fb7cb068 100644
--- a/release.nix
+++ b/release.nix
@@ -2,6 +2,7 @@
     (fetchTarball "https://github.com/oar-team/kapack/archive/master.tar.gz")
   {}
 , doCheck ? false
+, doCoverage ? true
 , batsim ? kapack.batsim
 , batsim_dev ? kapack.batsim_dev
 , batexpe ? kapack.batexpe
@@ -11,6 +12,7 @@ let
   pkgs = kapack.pkgs;
   pythonPackages = pkgs.python37Packages;
   buildPythonPackage = pythonPackages.buildPythonPackage;
+  optionOnOff = option: "${if option then "on" else "off"}";
 
   jobs = rec {
     # Batsched executable file (built from local sources)
@@ -24,12 +26,19 @@ let
         "^src/external/.*\.?pp"
         "^meson\.build"
       ];
+      mesonFlags = []
+        ++ pkgs.lib.optional doCoverage [ "-Db_coverage=true" ];
       nativeBuildInputs = with kapack; [pkgs.meson pkgs.ninja pkgs.pkgconfig
         pkgs.boost gmp rapidjson intervalset loguru redox pkgs.cppzmq pkgs.zeromq];
       # Debug build, without any Nix stripping magic.
       mesonBuildType = "debug";
       hardeningDisable = [ "all" ];
       dontStrip = true;
+      # Keep files generated by GCOV, so depending jobs can use them.
+      postInstall = pkgs.lib.optionalString doCoverage ''
+        mkdir -p $out/gcno
+        cp batsched@exe/*.gcno $out/gcno/
+      '';
     });
 
     # Batsched integration tests.
@@ -47,7 +56,13 @@ let
       buildInputs = with pkgs.python37Packages; [
         batsim batsched batexpe pkgs.redis
         pytest pytest_html pandas];
+      preBuild = pkgs.lib.optionalString doCoverage ''
+        mkdir -p gcda
+        export GCOV_PREFIX=$(realpath gcda)
+        export GCOV_PREFIX_STRIP=5
+      '';
       buildPhase = ''
+        runHook preBuild
         set +e
         (cd test && pytest -ra --html=../report/pytest_report.html)
         echo $? > ./pytest_returncode
@@ -64,6 +79,8 @@ let
       installPhase = ''
         mkdir -p $out
         mv ./report/* ./pytest_returncode $out/
+      '' + pkgs.lib.optionalString doCoverage ''
+        mv ./gcda $out/
       '';
     };
     # Essentially the same as integration_tests, but with an up-to-date Batsim.