The isolation policy¶
RPATH points at the prefix. The kernel and glibc are still the host’s. Loaded modules stay unless a site sets detect_loaded_modules to error or unload.¶
The environment is constructed. Host libraries on the node are not a dependency.¶
The isolation policy is a list of what the build may see. Ambient host libraries are not on that list.¶
What you will be able to do
Say what environment a build runs in, and what EasyBuild removes from it.
Say why a build that works in your shell can fail in a job, and the other way round.
Name what EasyBuild does not isolate, and what that means for reproducibility.
A build that succeeds on one workstation but fails on another is the most common support case in this domain. Nearly every occurrence has a single question. Determine what the shell might detect that the build does not, or vice versa. The remainder of the page describes what a build may observe. The explanation is an intentional policy with intentional gaps.
The environment is constructed, not inherited¶
EasyBuild does not build within the shell’s environment.
At session start, before any easyblock step, EasyBuild warns about
previously loaded modules by default. It does not unload them.
During prepare, EasyBuild loads the toolchain module and the dependency modules from A toolchain is a hierarchy’s hierarchy.
During prepare, EasyBuild sets the compiler variables itself.
The final step often surprises users.
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, 2026-09-10, 2025 module environment
Loading a toolchain loads the hierarchy. Those modules export
PATH, LD_LIBRARY_PATH, EBROOTFOSS, EBROOTGCC and the rest.
It does not set $CC, $CXX, $CFLAGS, $LIBBLAS, or
$LIBSCALAPACK.
These variables exist only within a build.
EasyBuild’s toolchain object sets them from the toolchain class and from toolchainopts.
What a toolchain injects is that page.
Loading a toolchain does not reproduce a build’s environment.
Debugging a compilation by loading foss and manually executing the failing command encounters 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 does not use module purge.
detect_loaded_modules defaults to warn.
ModulesTool.load is invoked with purge False.
Running module purge is documented as futile.
Running module purge is documented as broken on some systems (Cray).
A loaded EasyBuild module (one that exported $EBROOT*) that was not declared still triggers a warning.
The failure appears months later on a machine where that module is missing.
--filter-env-vars omits those names from the generated modulefile.
It does not strip them from the build environment.
Sites configure it for variables that break builds when inherited.
LD_LIBRARY_PATH is the common example.
A path in LD_LIBRARY_PATH overrides the toolchain’s provision without a message.
The build links 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¶
The build environment is constructed without the runtime environment.
The module is loaded by the user in the current shell.
A binary that finds its libraries through LD_LIBRARY_PATH at build time may fail to locate them at run time.
What “installed” means describes 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.
A module export creates a real modulefile that 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. Precise definition is required 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.
Nothing in an easyconfig records which glibc version was present.
The driver.
A GPU build links against a library belonging to the driver rather than to the toolchain.
libcuda.so.1 is exempt from the RPATH check by default
(--filter-rpath-sanity-libs).
sanity_check_paths never looks at it: that parameter only tests
files the easyconfig listed under the install prefix.
libcuda.so.1 is absent on a node without a GPU.
libcuda.so.1 is present on a node with a GPU.
No build can carry libcuda.so.1.
The filesystem and its layout.
An easyconfig that hardcodes a path outside its own install directory describes one machine.
The CPU.
optarch is on by default.
The compiler is told to optimise for the machine it is running on.
A binary built on one microarchitecture may not run on another.
A site with mixed hardware keeps separate install trees.
What “installed” means’s per-architecture lesson exists.
Anything declared as a system dependency.
osdependencies names packages EasyBuild expects the operating system to provide.
EasyBuild will not build packages listed in osdependencies.
That is the deliberate hole.
Some things belong to the distribution.
Listing them says they belong to the distribution.
This avoids pretending to control them.
EasyBuild isolates the software it built.
EasyBuild isolates the environment it built in.
EasyBuild does not isolate the machine.
A stack is reproducible against a fixed operating system and processor.
The same stack elsewhere requires handling when the operating system or processor are not fixed.
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.
What is the most likely difference, and which mechanism from this page is involved?
Whose build is wrong, and why is it the one that succeeded?
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. Their build found
the header through the ambient environment rather than through a declared
dependency. --filter-env-vars would not have stopped that: it only
omits names from the generated modulefile.
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 detect_loaded_modules
defaults to warn.
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.
What to remember
EasyBuild does not build in the shell’s environment.
detect_loaded_modulesdefaults towarn.ModulesTool.loadis called withpurgeFalse.module purgeis not the isolation mechanism, and it is documented as broken on Cray.