1. Foreword
Reproducibility of a research study in computer science has always been a complex matter. One of the biggest challenges is to recreate the original software environment. The latter is often built manually or using modules [1], especially on high-performance computing (HPC) platforms. The main issue with this approach is that modules and building instructions are likely to vary from one machine to another depending on system configuration and available software. Some package managers such as Spack [2] allow users to define their own package variants and leave them the choice of the configuration, the dependencies or the compiler. However, as the package manager still depends on compilers and other components provided by the underlying system, the reproducibility of software environments remains threatened. From this point of view, the container solutions such us Singularity [3] or Docker [4] are more robust but they do not make updating or management of multiple environment variants very easy. For example, if we want to use a different version of one or more packages, we either have to modify the container interactively, which would make it even less reproducible, or re-build it, which can take lot of time if performed regularly. Moreover, because the container is often based on an existing Linux distribution, we are limited to the versions of various core packages (compilers, MPI1, BLAS2, …) provided by that particular distribution in that particular release unless we want to manually re-build and re-configure a significant portion of the software environment. In the aim to cope with these limitations, take total control over our software environments and be able to adapt and reproduce them easily, we propose to explore and practice Guix [5], [6].
Another challenge to the reproducibility of an experimental research study is
the ability to rerun the associated computational experiments. We often need to
repeat experiments more than once, using different software versions and
algorithmic options, possibly months or even years apart. The importance of
maintaining a complete and well-structured documentation—allowing both ourselves
and others to recreate the experimental software environment, rerun all the
experiments, and post-process the results—is therefore critical. While a
detailed README file and thoroughly commented source code address this need to
some extent, we propose going further by adopting literate programming
[7], a paradigm that combines source code with a natural language
narrative. Among the many literate programming tools available, we will present
Org mode [8], [9] for the Emacs editor. With Org mode, we can
write a document that combines formatted text, images, and figures with blocks
of source code. These code blocks can be extracted as files [10],
for example to be compiled, or evaluated on the fly [11]. The results
of the evaluation—such as a figure or the return value of a computation—can then
be included directly in the document. We can export this document in various
formats [12], and share it with the community, whether as a
scientific publication, a research report, or a simple web page.
Finally, we will discuss the long-term availability of the elements of a research study and the associated source code, including external dependencies. Even when hosted in version-controlled repositories on a source code forge such as Inria Forge or BitBucket, they can suddenly become unavailable—for example, due to a repository migration or the permanent shutdown of the platform. We will therefore introduce the Software Heritage [13] and the Zenodo [14] projects, which are dedicated to the long-term archival of source code, experimental datasets and other artifacts.
2. Goals
The main focus of this tutorial is on the reproducibility of an experimental software environment. After a brief tour of Guix, Nix and other possible solutions, participants will learn the basics of software deployment with Guix by the means of a hands-on session.
Then, participants will use Guix to reproduce the experimental software environment of an existing research study and be able to observe its advantages over the aforementioned approaches.
Finally, participants will use Guix to build a reproducible experimental software environment from scratch.
Following the hands-on session, we will discuss how to improve the reproducibility of an experimental research study even further thanks to the association of a package manager such as Guix, the literate programming paradigm and an adapted long-term archival solution.
3. Target audience
This tutorial is intended for scientists and engineers looking for a complete software framework that enables them to:
- create reproducible software environments, whether for high-performance computing (HPC) experiments, to achieve reproducible research results, or to share development environments,
- maintain complete and structured documentation that makes it possible to reproduce a set of computational experiments along with the associated software environment,
- ensure the long-term availability of the components of an experimental scientific study.
A basic knowledge of GNU/Linux is required: processes, command line, and software installation.
4. Pre-requisites
The hands-on session can take place:
- on participants’ laptops, provided they have installed GNU Guix version 1.5.0
in advance by following the
instructions;
approximately 20 GiB of free space on the root partition (
/) will be needed during the hands-on session; - on the Grid'5000 experimental testbed [15] accessible to participants via SSH; in that case, participants will need to have an SSH client and know how to use it.
5. Workspace
For the needs of the tutorial, we have created a special project group, namely Publish a reproducible result (JRFRR 2026), on GitLab.com with the following structure:
Publish a reproducible result (JRFRR 2026)
├── Channel
├── Slides
├── Study
└── Tutorial
We address the Channel repository later in Section 6.4. The Tutorial
repository contains the sources of the present document and Slides the sources
of the companion slides (accessible by clicking the button on the left-side
panel of this page). We do not report further on these two repositories.
Finally, the Studies repository contains an example experimental resarch study
assessing the performance of minisolver [16], a simple
application for solving dense linear systems arising from aeroacoustic
simulations, by the means of a set of numerical experiments.
During the hands-on session, participants will work on the Studies repository
which has the following structure:
Study
├── .guix
│ ├── channels.scm
│ └── manifest.scm
├── .gitattributes
├── .gitignore
├── .gitlab-ci.yml
├── AUTHORS
├── LICENSE
├── README.md
├── benchmarks.csv
├── cylinder.png
├── plot.py
├── references.bib
├── results.csv (LFS)
└── study.tex
The .guix folder contains everything one needs to reproduce the experimental
software environment of the study using Guix. The environment contains the
necessary software packages to:
- run the set of numerical experiments involving
minisolveraccording to the parameters defined inbenchmarks.csv, where each line represents one experiment, and store the results intoresults.csv; - post-process
results.csv, i.e., format data and draw plots, using theplot.pyPython script; - produce the PDF version of a manuscript presenting and analyzing the
experimental results of the study by compiling its LaTeX source files, i.e.,
study.texandreferences.bib.
The main branch contains the complete configuration participants will
reproduce in the first part of the hands-on session. The learn branch
represents an incomplete configuration participants will complete in the second
part of the session.
6. Hands-on session
In the first place, we will put the experimental studies aside and familiarize ourselves with Guix.
6.1. First use of Guix
If you plan to use Guix on the Grid'5000 platform, connect over secure shell (SSH) to the frontal node in Lille (see configuration in the e-mail from Simon Tournier):
ssh -Y lille.g5k
and request a computational node from the Chiclet cluster:
oarsub -I -t inner=2145873 -l host=1,walltime=02:00:00 --project lab-2026-jrfrr
From here, you can jump directly to Section 6.2.
Here, we assume that we are running a third-party Linux distribution such as Debian, Fedora or Manjaro.
After the installation (see
instructions),
we proceed with a short sequence of commands to ensure a smooth user experience
with Guix onward. At the beginning, we install our first package using Guix,
i.e. glibc-locales to allow the system to switch locales.
guix install glibc-locales
Then, to be able to acquire new versions of installed packages, we will need to pull the latest version of Guix first. The following command can take a while to execute, especially when run for the first time.
guix pull
Once the process finishes, we need to follow the hint the command gives us and
add the following lines to our .bash_profile or .bashrc to always get access
to the most recent Guix built by guix pull.
GUIX_PROFILE="$HOME/.config/guix/current"
. "$GUIX_PROFILE/etc/profile"
We also have to tell to our shell to use this new Guix.
hash guix
Finally, we can update our installed packages.
guix upgrade
To get information on the generation (revision in Guix terminology) of Guix being used, we can use:
guix describe
6.2. Familiarization with Guix
Let us enter our first Guix environment containing two packages, bash and
cowsay, using the guix shell command and launch a shell inside of that
environment. Here, we use the --container or -C switch to span the new
environment within an isolated container. By default, we don't have access to
host filesystem (except for the current working directory), to host network or
environment variables. See guix shell --help for more details.
guix shell --container bash cowsay -- bash
To test the cowsay package within the Guix shell, try to type cowsay "Hello
world", for example.
On some systems, using --container fails with an error along these lines:
$ guix shell --container bash cowsay guix shell: error: clone: 2114060305: Invalid argument
This indicates that the system lacks support for Linux's unprivileged user
namespaces. Worry not: you can fall back to --pure, which is weaker, but
still gives good control over the environment. See
documentation
for more details.
We can simply type exit to get back to our original shell. Also, we do not
have to run an interactive shell inside of the environment. We can directly
execute a given command like for example:
guix shell --container cowsay -- cowsay "Hello world"
Note that we did not include bash this time, we did not need it. The above
command line should give us the following output:
______________
< Hello world >
--------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
6.2.1. Manifests
The guix shell command seems very convenient. However, let us imagine that we
do not need only two but 26 packages in our environment. The command line would
become quiet long, right? The good news is that we can instead put our list of
packages into a file, referred to as manifest, then use the --manifest or -m
option to pass the manifest to our guix shell command line.
Manifest files [17] use the Scheme language [18]
syntax which can be intimidating in the beginning. Fortunately, guix shell has
got the --export-manifest option allowing one to automatically generate the
manifest file corresponding to the environment specified on the command line.
Let us thus create the manifest corresponding to our latest single-package
environment and save it to a Scheme file named cowsay.scm.
guix shell --export-manifest cowsay > cowsay.scm
Our manifest should look like this. Not so scary in the end, is it?
(specifications->manifest
(list "cowsay"))
Finally, we can enter the target environment using the manifest file and retry
a cowsay command.
guix shell --container -m cowsay.scm -- cowsay "Hello from the manifest"
6.2.2. Channels
Note that, by default, guix shell considers the latest versions of the
specified packages available in our current revision of Guix. Well, maybe we are
fine with that at this point but what happens if we want to enter the exact same
environment on a different computer or a couple of weeks, months, years later?
Maybe the packages will not be even available anymore, or at least, not in the
same version.
Software packages in Guix are provided through dedicated git repositories called
channels [19]. The official Guix channel guix, automatically
set up in our Guix installation, currently provides more than 31,000 packages
[20]. However, many other channels are available, e.g., for
scientific HPC software and so on. We will discuss the usage of multiple
channels later. For the moment, let us focus on the default guix channel.
To ensure the same revision of Guix providing the same packages in the same versions, we can accompany our manifest with a channel file, also written in Scheme. In the latter we can specify the channel or channels to use together with the desired revision number, i.e., commit.
We can obtain the currently used commit of the guix channel (and other
channels, if any) by typing guix describe. The output should look like
follows, modulo the language, date and time ;-).
Pokolenie 1 17. december 2024 02:27:16 (súčasné)
guix c3290ce
zdroj repozitára: https://git.savannah.gnu.org/git/guix.git
vetva: master
úprava: c3290cee6add60b7e56f5f919d9498d78542790a
Using the -f option of guix describe, we can generate the corresponding
channel file, e.g. channels.scm, as follows.
guix describe -f channels > channels.scm
The contents of channels.scm then should look like this.
(list
(channel
(name 'guix)
(url "https://codeberg.org/guix/guix.git")
(branch "master")
(commit
"c3290cee6add60b7e56f5f919d9498d78542790a")
(introduction
(make-channel-introduction
"9edb3f66fd807b096b48283debdcddccfea34bad"
(openpgp-fingerprint
"BBB0 2DDF 2CEA F6A8 0D1D E643 A2A0 6DF2 A33A 54FA")))))
Finally, to execute the guix shell command using our channel file, we can use
the guix time-machine command with the --channels, or -C, option.
guix time-machine --channels=channels.scm -- shell --container --manifest=cowsay.scm -- cowsay -f tux "I <3 Reproducibility"
The output should look like this:
______________________
< I <3 Reproducibility >
----------------------
\
\
.--.
|o_o |
|:_/ |
// \ \
(| | )
/'\_ _/`\
\___)=(___/
If it does, it means that you have at your hands everything you need to create and deploy reproducible software environments with Guix.
Excellent!
6.3. Reproducing an environment
We are now ready for the core part of the hands-on session. At first, we are
going to reproduce the experimental research study on the minisolver
application from Section 5 including all the numerical experiments
and the
final
manuscript.
To begin, we need to clone the corresponding Study repository.
git clone https://gitlab.com/jrfrr-2026-p2r/study.git
If you want to keep a copy of the repository with all you'll have done during the session, feel free to fork it first!
Then, we navigate to the root of the local clone.
cd study
Inside, the .guix folder contains the channel file as well as the manifest
file specifying the original experimental software environment of the study. Our
goal is to deploy this environment thanks the association of the guix
time-machine and guix shell commands discussed in Section 6.2 so
as to:
reproduce the experiments (parameters in
benchmarks.csv, results inresults.csv),guix time-machine --channels=.guix/channels.scm -- shell --container --manifest=.guix/manifest.scm -- minisolver --batch-input benchmarks.csv --batch-output results.csvpost-process the results (from
results.csvinto the plotssequential.pdfandparallel.pdf),guix time-machine --channels=.guix/channels.scm -- shell --container --manifest=.guix/manifest.scm -- python3 plot.pyreproduce the manuscript of the study (featuring the plots
sequential.pdfandparallel.pdf).guix time-machine --channels=.guix/channels.scm -- shell --container --manifest=.guix/manifest.scm -- latexmk -f -pdf -bibtex -interaction=nonstopmode study
To visualize the resulting manuscript including the reproduced experimental results, you can use the command below.
guix shell xpdf -- xpdf study.pdf
Rather straightforward, isn't it?
6.4. Building an environment
We are now going to practice the construction of reproducible software
environments, i.e., the preparation of manifests and channel files as seen in
Section 6.2, but on a more complex example involving multiple
packages and more than the default guix channel.
We continue to work on the minisolver experimental study, but we are going
to switch to the learn branch
git checkout learn
where the .guix folder is empty. In this part of the tutorial, our goal is
to use Guix to:
- build a software environment featuring multiple packages from multiple channels,
- capture and preserve the description of the final environment in the form of a manifest and a channel files so as to ensure the reproducibility of the environment on another copmputer or at another time.
6.4.1. Towards a manifest file
The numerical experiments of the study require only the minisolver
application to run. However, minisolver is not (yet) available from the
default guix channel. It is provided by a
custom channel.
Follow the instructions in the README.md file of the channel (section 'How
does it work?') to be able to access the packages it provides. To check the
availability of the minisolver package in your profile, try to spawn an
environment containing minisolver and run a quick experiment.
guix shell --container minisolver -- minisolver --size 1000
If everything worked well, the standard output should end with something like:
[minisolver] relative error = 1.6739e-14 [minisolver] test-specific cleaning ... done [minisolver] computation completed [minisolver] cleaning ... done [minisolver] total computation time = 0.046672 s [minisolver] memory usage peak = 6 MiB
Do you remember the option of guix shell that allows one to produce the
manifest file corresponding to the current command line? If you need a
refresher, have a quick look on Section 6.2.1. Otherwise, create
the manifest describing the previous environment with minisolver and save
the result to minisolver.scm. The result should look like this:
;; What follows is a "manifest" equivalent to the command line you gave.
;; You can store it in a file that you may then pass to any 'guix' command
;; that accepts a '--manifest' (or '-m') option.
(specifications->manifest (list "minisolver"))
We are now going to enrich this environment with the following packages in order to be able to post-process the experimental results and produce the final manuscript:
packages to perform basic operations,
bash coreutils whichpackages to manipulate and plot experimental results,
python python-pandas python-matplotlibpackages to produce the final manuscript written in LaTeX.
texlive-scheme-basic texlive-collection-fontsrecommended texlive-type1cm texlive-underscore texlive-dvipng texlive-babel-english texlive-latexmk texlive-wrapfig texlive-ulem texlive-capt-of texlive-listings texlive-fancyvrb texlive-upquote texlive-lineno texlive-biblatex texlive-biber texlive-xcolor
The list of Texlive packages is a bit long. Indeed, we could have simply used
the global texlive package, but the latter weights almost 4 GiB and provides a
lot of LaTeX packages we do not need.
Use the guix shell --container command to deploy a new environment featuring
minisolver as well as all the aforementioned packages and spawn a Bash
shell in it.
Then, from within the environment:
run the experiments (parameters in
benchmarks.csv, results inresults.csv),minisolver --batch-input benchmarks.csv --batch-output results.csvpost-process the results (from
results.csvinto the plotssequential.pdfandparallel.pdf),python3 plot.pyproduce the manuscript of the study (featuring the plots
sequential.pdfandparallel.pdf).latexmk -f -pdf -bibtex -interaction=nonstopmode study
If everything worked well, quit the environment using the exit command and
export the corresponding manifest into .guix/manifest.scm.
To visualize the resulting manuscript including the reproduced experimental results, you can use the command below.
guix shell xpdf -- xpdf study.pdf
6.4.2. Channels
As we state in Section 6.2.2, the manifest file alone is not enough to ensure the reproducibility of a software environment built with Guix. The second ingredient is a channel file capturing the sources and the versions of the channels in use while building the environment.
Do you remember the guix describe command that allows one to retrieve the
information about the channels currently in use? Let us try.
The result should look like the following. Notice the presence of the
jrfrr-2026-p2r channel we added in Section 6.4.1. The commits
of both channels changed too!
Generation 1 May 18 2026 11:26:33 (current)
jrfrr-2026-p2r d14fee4
repository URL: https://gitlab.com/jrfrr-2026-p2r/channel.git
branch: main
commit: d14fee41b8f536f089916df9a39432216cfec2cc
guix aa16403
repository URL: https://git.guix.gnu.org/guix.git
branch: master
commit: aa164034801fab25681cac772c2dcd4b8498d8ad
And what about the option conveniently producing the output in a form of a
channel file? If you need a refresher, have a quick look on
Section 6.2.2. Otherwise, capture the current channel
configuration into .guix/channels.scm and test both your channel and your
manifest files using:
guix time-machine --channels=.guix/channels.scm -- shell --container --manifest=.guix/manifest.scm -- minisolver --size 1000
If everything worked well, the standard output should end with something like:
[minisolver] relative error = 1.6739e-14 [minisolver] test-specific cleaning ... done [minisolver] computation completed [minisolver] cleaning ... done [minisolver] total computation time = 0.046672 s [minisolver] memory usage peak = 6 MiB
6.5. Modular environment
Now that we have reached the goal of the hands-on session, let us go a little
bit further in our usage of the guix shell command. The latter provides the
so-called transformation options, such as --with-input, allowing one to tweak
the resulting software environment without further manual intervention.
For instance, the --with-input=<a>=<b> option replaces the package <a> by
the package <b> in the entire dependency graph of the software environment
spawned by the corresponding guix shell command without having to manually
recompile the packages depending on <a>.
Let us illustrate this particular option on a concrete example. The
custom channel (see Section
6.4.1) provides the fibonacci package. As its name suggests, it
computes the Fibonacci sequence for a given input value, e.g., Fibonacci(8)
= 21. To perform the computation, it resorts to the libfibonacci library.
This library has two implementations: a naive recursive implementation provided
by the package libfibonacci-recursive as well as a significantly faster
iterative implementation provided by the libfibonacci-iterative package. Both
are available from the same channel as the fibonacci package itself.
By default, fibonacci links to the slower libfibonacci-recursive
implementation. For example, if we try to compute Fibonacci(48) like so,
guix shell --container fibonacci -- fibonacci 48
the program takes several seconds to compute the result:
Implementation : Recursive Fibonacci Fibonacci(48) : 4807526976 Computation time : 5 s
Try to apply the --with-input option on the above guix shell command so as
to swap libfibonacci-recursive for libfibonacci-iterative in the resulting
environment. Then, compute Fibonacci(48) again and observe the difference!
Although this is a very simple example, the --with-input option becomes handy
in many real-life situations. In high-performance computing, we often swap
between multiple implementations of the same communication library, such as
MPI1, the same linear algebra routines, such as BLAS2 or LAPACK
[21], and so on.
Do not hesitate to try out to apply the --with-input option of guix shell on
the experimental software environment involving minisolver. The latter
uses the BLAS2 and LAPACK [21] linear algebra routines. By
default, it links to the open-source OpenBLAS implementation of those
[22] (package openblas in the guix channel). However, in
practice, we often resort to vendor-specific implementations such as the Math
Kernel Library (MKL) [23] from Intel(R) (package intel-oneapi-mkl in
the
guix-science-nonfree
channel).
7. Pointers
In addition to the bibliography at the end of the document, the following pointers may be of interest for those who would like to learn further on how to use Guix, Org mode for Emacs or long-term archival solutions:
- https://guix-org-tutorial-compas-2025.gitlab.io/tutorial/ (Guix and Org mode tutorial, ComPAS'2025, Bordeaux, France)
- https://felsoci.sk/blog/posts.html (blog of Marek Felšöci with posts on Guix and Org mode usage - for work and for home)
- https://hpc.guix.info/ (Guix-HPC, reproducible software deployment for high-performance computing: channels, packages, events, …)
- https://cours-mf.gitlabpages.inria.fr/is328/tuto-chameleon.html (tutorial on how to use Guix or Singularity images produced by Guix on HPC platforms such as PlaFRIM)
8. References
Footnotes
Message Passing Interface - a message-passing library interface specification addressing primarily the message-passing parallel programming model, in which data is moved from the address space of one process to that of another process through cooperative operations on each proces.
Basic Linear Algebra Subprograms - routines that provide standard building blocks for performing basic vector and matrix operations.
Last modified on 18/05/2026 at 22:55.
This page was crafted in Org mode for Emacs.
The content is available under the terms of the Creative Commons BY NC 4.0 International license unless otherwise stated.