Table of Contents
Why Environment Modules Exist
On most HPC systems there are many versions of the same software. There may be several compilers, multiple MPI libraries, and many builds of the same application with different options. Installing everything in a single fixed configuration does not work well. Users need to choose which version and combination they want, often from the command line and without administrator rights.
Environment modules solve this problem by changing your shell environment on demand. They let you load and unload software configurations in a reversible and controlled way. Instead of editing variables such as PATH or LD_LIBRARY_PATH manually, you ask the module system to do it. This is especially important on shared clusters where the system-wide environment must remain generic and safe.
An environment module describes one software package or one configuration of a package. When you load a module the module system changes environment variables for your shell session so that the corresponding software becomes visible and usable. When you unload it the environment is restored as if the module was never loaded.
Environment modules do not install software. They only adjust environment variables so that existing installations become usable in your session.
Basic Concepts and Terminology
The core ideas of environment modules are simple but the terminology is specific. A module file is a small text file that encodes how to modify the environment. It can add directories to PATH, LD_LIBRARY_PATH, MANPATH, and other variables, set new variables like CC or MPI_HOME, and sometimes run checks or print messages.
All module files are stored in one or more module paths. The module path is a list of directories that the module system searches to find modules. This list is stored in an environment variable, typically MODULEPATH. System administrators control the default module paths, but advanced users can extend them.
The module system itself is a program that your shell uses, usually called as the module command or its modern equivalent such as ml. It is not a standalone executable that runs and exits. Instead it is integrated into your shell so that it can change environment variables in your current session.
Finally, a module name identifies a specific module file. Names are often hierarchical, for example gcc/12.2.0 or openmpi/4.1.6-gcc-12.2.0. The part before the slash is often the software, and the part after the slash is the version or build variant.
Common Module Systems in HPC
Most HPC centers use one of two major module families. The original Environment Modules system is often provided as the module command. It uses module files written in the Tcl language. Many clusters still use it because it is mature and stable.
A more modern alternative is Lmod. Lmod is Lua based and supports hierarchical module naming, automatic dependency handling, and more advanced features. On many systems the module or ml command is actually Lmod underneath, even if the syntax looks similar.
For an end user the basic commands are deliberately similar across these implementations. The exact formatting of output, advanced features, and the internal language of module files differ, but for simple tasks such as listing, loading, and unloading modules you can usually follow the same patterns on any cluster.
Inspecting Available Modules
On a new system your first interaction with modules is usually to discover what is installed. The module system provides commands for this. The most common one is:
module avail
This lists all modules that are currently visible in your MODULEPATH. The output is often grouped by directory, with lines showing module names like gcc/11.3.0 or intel/2023.1. On Lmod systems you might see a warning if the output is long, and you can filter it by name, for example:
module avail gcc
This typically lists only modules whose names include gcc. Some systems provide an even more flexible search, for example:
module spider
module spider openmpi
On systems with hierarchical modules module spider can show you modules that are not yet visible because they depend on other modules. It will tell you what to load first to make a certain module available.
If you cannot find a module with module avail or module spider it is almost never enough to modify PATH manually. Instead, check documentation or support channels. The software may be named differently or provided from a different module path.
Viewing and Understanding a Module
Before you load a module you may want to know what it will do. The standard way is:
module show gcc/12.2.0
This prints a description of the module and how it will change your environment. You might see lines that add a directory to PATH, set variables like CC=gcc, or adjust library paths. On some systems the equivalent is module display.
The output of module show is a translation of the internal module file, not the file itself, but it shows the important effects. You do not have to understand the module language to interpret this output. You mainly look for which variables are changed and which other modules are required or conflicted.
Advanced users can look directly at the module file with module cat on Lmod or by opening the file path printed by module show. This is useful if you want to create your own module files modeled on existing ones, which is typically introduced only when you are comfortable with the basics.
Loading and Unloading Modules
To actually use software, you load its module into your session. This is typically done with:
module load gcc/12.2.0
After this, running gcc --version in the same session should show the version from the module. If another incompatible compiler was previously loaded, the cluster’s module configuration might either refuse to load the new one or automatically swap it.
You undo this change with:
module unload gcc/12.2.0After unloading, your environment is restored to the previous state. If the module was loaded as part of a chain of dependencies you might need to unload dependent modules first, depending on how the system is configured.
Some module systems provide a shortcut for replacing one module with another:
module swap gcc/11.3.0 gcc/12.2.0This tries to remove the first and load the second in a single step. It is useful when you want to test a different version without losing dependent modules.
Always modify your environment with module load, module unload, or module swap. Avoid manual edits to critical variables such as PATH and LD_LIBRARY_PATH in long sessions. Manual changes are easy to forget and hard to reproduce.
Querying and Saving Your Current Module State
It is often useful to know which modules are currently active. The module system can tell you this with:
module list
This lists all loaded modules in the order they were loaded. The order can matter because some modules prepend to PATH and similar variables. Later modules may override earlier ones.
For reproducible work you often want to capture this state. A simple approach is to copy the module list output into your run notes or script comments. Some module systems also provide commands to save and restore named collections. On Lmod, for example:
module save mysetup
module restore mysetupThis writes your current loaded modules to a named collection and later restores them in another session. This is helpful when you have a stable working configuration and do not want to remember each individual module.
Modules and Environment Variables
Although the module system hides the complexity, it is important to understand at a conceptual level what happens when a module is loaded. The module system reads the module file and then applies operations to environment variables in your shell. The most common actions are:
- Prepending or appending directories to list variables such as
PATH,LD_LIBRARY_PATH,CPATH, andMANPATH. For example, a compiler module might run an internal command equivalent to:
export PATH=/path/to/compiler/bin:$PATH- Defining new variables that point to the software installation, such as
HDF5_ROOTorMKLROOT. - Defining helper variables like
CC,CXX, orFCso that build systems automatically pick the right compiler wrappers. - Setting variables that influence behavior of the software itself, such as licensing configuration or debug level, though good module design usually keeps such effects minimal and explicit.
Unloading the module reverses these operations. The module system keeps track of which changes belong to which module so that it can remove them safely.
Modules modify your shell session only. If you open a new terminal or start a new batch job without loading the same modules, your environment will not match. Always remember that module changes are not global and not permanent.
Hierarchical and Dependency Aware Modules
On modern clusters, especially with Lmod, modules are often organized hierarchically. Instead of exposing every possible MPI and library build at once, the module system shows you different modules depending on what is already loaded.
A common pattern is that core modules like compilers are always visible. Once you load a compiler, MPI modules compatible with that compiler become visible. After you load a specific MPI, more specialized libraries and applications built against that MPI appear.
This behavior is often driven by module dependencies encoded in the module files. For example, a module file for openmpi/4.1.6-gcc-12.2.0 might declare that it depends on gcc/12.2.0. When you load the MPI module, the module system will ensure that the compiler module is loaded as well or will refuse if an incompatible compiler is already loaded.
This design guides you towards compatible combinations and reduces the risk of subtle bugs caused by mixing mismatched components.
Using Modules in Interactive Sessions
During interactive work you often experiment with different compilers and libraries. A typical workflow could be:
- Start from the default environment provided when you log in.
- Use
module availto inspect compiler options. - Load a compiler, for example
module load gcc/12.2.0. - Use
module availagain to see MPI options for that compiler. - Load an MPI, for example
module load openmpi/4.1.6-gcc-12.2.0. - Compile and test your code.
- If you want to test another combination, unload or swap modules and repeat.
If you get confusing error messages, such as executables not found or wrong versions of libraries being used, check module list. Unexpected entries there are often the source of surprising behavior, especially if you imported environment settings from a previous session or copied lines from someone else’s script.
Using Modules in Batch and Job Scripts
On an HPC cluster, most serious runs happen inside batch jobs managed by a scheduler. In this context, you must ensure that your job script sets up the environment before running your application. That means you typically load the same modules in your job script that you used when building and testing the code interactively.
A minimal pattern inside a job script is:
module purge
module load gcc/12.2.0
module load openmpi/4.1.6-gcc-12.2.0
The module purge clears any default modules so that the environment is entirely determined by your script. This improves reproducibility between runs and across systems, as it eliminates surprises from evolving default settings.
After loading modules, you can safely invoke compilers, MPI launchers, and applications that rely on those modules. If you later change compilers or library versions, update the corresponding module load lines and rebuild if necessary.
Always record your module load sequence in your batch scripts. Do not rely on interactive module settings when submitting jobs. Scheduler environments are usually clean and may not inherit your login shell configuration.
Site Specific Features and Policies
Although basic module commands are standard, each HPC center may apply its own policy and structure. Some sites define a standard module set that is loaded at login, for example a default compiler and MPI. Others provide an almost empty environment and expect you to choose everything explicitly.
Some sites use naming schemes that encode toolchains and options, such as foss/2023a or intel-mpi/2021.9. These represent consistent stacks of compilers, MPI, numerical libraries, and utilities. Loading such a module brings in a large, compatible collection in one step. The details of these schemes are site specific, and you should consult site documentation for interpretation.
Systems may also provide helper modules that configure common workflows, for example modules that set up Python environments, GPU toolchains, or specific application suites. Even if they appear complex, they are built on the same module mechanism and ultimately set environment variables for you.
Creating and Managing Your Own Modules
At some point you may install your own software in your home or project directory. You can still use the module system to manage it. This involves writing your own module files and placing them in a directory that you add to MODULEPATH.
This process usually follows a pattern:
- Create a directory for your personal modules, for example
$HOME/modules. - Add it to your module path in your shell startup file, for example:
module use $HOME/modules- Write simple module files that set or prepend the necessary environment variables for your software.
- Load your custom modules with
module loadas usual.
The language used inside module files depends on the module system, typically Tcl for classic modules and Lua for Lmod. For beginners, copying and adjusting a simple example provided by your site or colleagues is often the easiest way to start. Writing module files is usually introduced after you are comfortable consuming system provided modules.
Reproducibility and Documentation through Modules
Environment modules are central to reproducibility on HPC systems. The combination of loaded modules directly defines which compilers, libraries, and tools your code uses. If you can reconstruct that combination, for example by recording it in a job script, you can often reproduce behavior and performance even long after the run.
It is good practice to include explicit module commands in any instructions, build notes, and project documentation. For example, you might record in a README:
module purge
module load gcc/12.2.0
module load openmpi/4.1.6-gcc-12.2.0
module load hdf5/1.14.0-openmpi-4.1.6-gcc-12.2.0This makes it clear what environment is required. If the cluster later removes these specific versions, a future user can at least see the intended stack and choose the closest available alternatives.
By relying on modules rather than hard coded paths, your workflows also adapt more easily when a site upgrades or restructures its software tree. The module system absorbs most of these changes, while module names and high level commands remain relatively stable.