What a build is allowed to see

What you will be able to do

  1. Say what environment a build runs in, and what EasyBuild removes from it.

  2. Say why a build that works in your shell can fail in a job, and the other way round.

  3. Name what EasyBuild does not isolate, and what that means for reproducibility.

A build that succeeds on one workstation and fails on another constitutes the most common support case in this domain. Almost every occurrence reduces to a single question. What might the shell detect that the build does not, or conversely? Therefore the rest of the chapter is what a build is permitted to observe. The explanation is a policy that is intentional and contains intentional gaps.

The environment is constructed, not inherited

EasyBuild does not build within the shell’s environment. During prepare, EasyBuild unloads previously loaded modules, loads the toolchain module and the dependency modules from A toolchain is a hierarchy’s hierarchy, and sets the compiler variables itself. The final step often surprises users, therefore it is measured rather than asserted.

What a toolchain module does and does not export

$ module purge && module load 2025 foss/2025a
$ echo "CC=[$CC] CFLAGS=[$CFLAGS] LIBBLAS=[$LIBBLAS]"
CC=[] CFLAGS=[] LIBBLAS=[]

$ echo "EBROOTFOSS=[$EBROOTFOSS]"
EBROOTFOSS=[/sw/arch/RHEL9/EB_production/2025/software/foss/2025a]

Recorded: SURF Snellius, int4 interactive node, 2026-09-10, 2025 module environment

Loading a toolchain provides EBROOT and EBVERSION and nothing else. It does not provide $CC, $CXX, $CFLAGS, $LIBBLAS, or $LIBSCALAPACK. These variables exist only within a build, where EasyBuild’s toolchain object sets them from the toolchain class and from toolchainopts. This is the subject of What a toolchain injects. The relevant implication for this chapter is that loading a toolchain does not reproduce a build’s environment. Attempts to debug a compilation by loading foss and manually executing the failing command encounter this limitation. Few discover why the command behaves differently. Standing where the build stood explains how to obtain that environment in practice.

What gets removed, and why

Two mechanisms exist. The default is not module purge. detect_loaded_modules defaults to warn, and ModulesTool.load is invoked with purge False. Running module purge is documented as futile and broken on some systems (Cray). A loaded module that was not declared still triggers a warning. The failure appears months later on a machine where that module is missing. --filter-env-vars removes specified variables from the build environment. Sites configure it for variables that break builds when inherited. LD_LIBRARY_PATH is the common example: a path in it overrides the toolchain’s provision without a message, causing the build to link against an undeclared library. An escape hatch, --allow-loaded-modules, lists modules that may remain loaded. It exists. It is also the first thing to check when a build is reproducible for one user but not for another.

Which is why RPATH matters

When the build environment is constructed, the runtime environment is not. A user loads the module in whatever shell is used. a binary that finds its libraries through LD_LIBRARY_PATH at build time may not locate them at run time. What “installed” means explains how that failure reaches a user. EasyBuild provides RPATH linking, enabled by default in recent versions. The path to each library is recorded in the binary itself. The module still sets LD_LIBRARY_PATH. The binary does not depend on LD_LIBRARY_PATH being set correctly. What a module exports presents a real modulefile that both prepends LD_LIBRARY_PATH and belongs to a tree whose binaries carry their own RPATH.

What is not isolated

The term “reproducible” is often used loosely, requiring precise definition here. The kernel and the C library. A build links against the system glibc. Two machines with different glibc versions can produce binaries that are not interchangeable, and nothing in an easyconfig records which one was present. The driver. A GPU build links against a library belonging to the driver rather than to the toolchain. That is why libcuda.so.1 is exempt from the sanity check by default. It is absent on a node without a GPU and present on one with, and no build can carry it. The filesystem and its layout. An easyconfig that hardcodes a path outside its own install directory is describing one machine. The CPU. optarch is on by default, so the compiler is told to optimise for the machine it is running on. A binary built on one microarchitecture may not run on another. That is why a site with mixed hardware keeps separate install trees, and why What “installed” means’s per-architecture lesson exists at all. Anything declared as a system dependency. osdependencies names packages EasyBuild expects the operating system to provide and will not build. It is the deliberate hole. Some things do belong to the distribution, and listing them says so instead of pretending to control them. So what EasyBuild isolates is the software it built and the environment it built in. What it does not isolate is the machine. A stack is reproducible against a fixed operating system and processor. The same stack somewhere else is about what to do when those are not fixed either.

EB-Isolation-1 — Works for me, fails for the desk

A user reports that their package builds fine in their own prefix and fails for the desk on the same cluster, in the same generation, from the same easyconfig. The error is a missing header from a library that is not in dependencies.

  1. What is the most likely difference, and which mechanism from this chapter is involved?

  2. Whose build is wrong, and why is it the one that succeeded?

  3. What is the fix, and what is the wrong fix that would make the symptom go away?

Solution

The user has something loaded that the desk does not, or has a path in an environment variable the desk’s build filters out. Their build found the header through the ambient environment rather than through a declared dependency.

The build that succeeded is the wrong one. It depends on something the easyconfig does not state, so it is not reproducible: it works in that shell, on that day, and will fail for the next person and for the same person after a system change. The desk’s failure is the correct behaviour, and it is the whole reason module purge is there.

The fix is to add the library to dependencies, so prepare loads it and the build finds it through something written down.

The wrong fix is --allow-loaded-modules for the module the user happened to have, or unsetting the filter so the ambient path comes back. Both make the symptom go away and leave the easyconfig still lying about what it needs.