Lmod Migration¶
An audit of Bodhi's current environment-modules setup and a phased plan for migrating to Lmod, the Lua-based module system.
Audience
This is an admin/infrastructure document. Migrating the module system is
a cluster-wide change that affects every user's module load; it is not a
per-user action. Nothing here should be executed without coordinating a
maintenance window (see Admin Guide).
TL;DR¶
Bodhi already runs environment-modules 5.3.0 — the modern Tcl implementation,
not the legacy 3.x most Lmod migration horror stories describe. The software tree
is small (~124 packages), flat, and syntactically simple. Lmod reads Tcl
modulefiles natively, so no bulk rewrite to Lua is required. This is a
low-complexity migration whose main payoff is ergonomics (ml, module
spider, user collections) and alignment with the wider HPC world — not a
performance necessity at this scale.
Current state (audit)¶
| Property | Value |
|---|---|
| Implementation | environment-modules 5.3.0 (2023-05-14), Tcl |
modulecmd |
/usr/share/Modules/libexec/modulecmd.tcl |
| Tcl available | 8.6 (/usr/bin/tclsh) |
| Lua available | 5.4.4 (/usr/bin/lua) |
| Lmod present | No |
| Distinct software packages | ~124 |
Modulefiles (modules-sw) |
237 files across 125 subdirs |
| Lua modulefiles | 0 (all Tcl) |
| Module hierarchy | None (flat MODULEPATH) |
MODULEPATH¶
Seven entries are configured, but only two are populated:
/etc/scl/modulefiles # system (empty on Bodhi)
/usr/share/Modules/modulefiles # system defaults
/etc/modulefiles # empty
/usr/share/modulefiles # empty
/cluster/software/modules-sw/modulefiles # ← main software tree (237 files)
/cluster/software/modules-perl/modulefiles # 1 file
/cluster/software/modules-openmpi/modulefiles # ← MISSING directory (stale entry)
Stale MODULEPATH entry
/cluster/software/modules-openmpi/modulefiles is in MODULEPATH but the
directory does not exist. Harmless today, but it should be removed as part of
the cleanup — Lmod will warn about missing MODULEPATH components.
Modulefile syntax profile¶
A scan of all modules-sw modulefiles for Lmod-relevant constructs:
| Construct | Files | Lmod compatibility |
|---|---|---|
prepend-path |
128 | ✅ native |
append-path |
6 | ✅ native |
setenv |
11 | ✅ native |
module-whatis |
131 | ✅ native |
proc ModulesHelp / puts stderr |
132 | ✅ native (help block) |
module-version … default (in .modulerc) |
104 | ⚠️ works, validate (see below) |
if {…} / [ … ] Tcl logic |
34 | ⚠️ runs via Tcl bridge, spot-check |
module load inside a modulefile |
35 | ⚠️ works; prefer depends-on |
exec |
1 | ⚠️ review (google-cloud-sdk) |
conflict |
0 | — |
prereq |
0 | — |
module switch / set-alias / source-sh / system |
0 | — |
The overwhelming majority of files are trivial PATH prepends. Representative
example (bwa/0.7.19):
#%Module1.0#####################################################################
proc ModulesHelp { } {
puts stderr "\tAdds BWA 0.7.19 to environment paths\n"
}
module-whatis "loads BWA 0.7.19 module"
set bwaroot /cluster/software/modules-sw/bwa/bwa-0.7.19
prepend-path PATH $bwaroot
prepend-path PATH $bwaroot/bwakit
Default-version mechanism¶
Defaults are set with 104 .modulerc files, each of the form:
#%Module1.0#####################################################################
module-version bcftools/1.22.1 default
There are no .version files (the older, deprecated mechanism) — good. Lmod
honors module-version … default from .modulerc, but default resolution
differs subtly from Tcl Modules, so every default must be validated after the
switch (see Phase 3).
Issues to fix before/during migration¶
These are the only non-mechanical items the audit surfaced:
trimmomatic/0.38is missing the#%Modulemagic cookie. Lmod requires the#%Modulefirst-line cookie to recognize a Tcl modulefile; without it the file will misparse or be skipped. Add the cookie.google-cloud-sdk/544.0.0usesexecto run a bash completion include at load time: This is fragile under any module system (its stdout is discarded; it doesn't actually source completions into the user's shell). Rewrite to notexecat load — drop it, or ship the completion via a shell profile snippet.- 35 modulefiles chain
module load(e.g.RingMapper/1.3loadspython/2.7.18viais-loaded+module load). These work under Lmod, but the idiomatic and safer form isdepends-on, which reference-counts the dependency so it unloads cleanly. Optional but recommended. - Remove the stale
modules-openmpiMODULEPATH entry.
Why Lmod (and the honest ROI)¶
Wins that apply at Bodhi's scale:
mlshorthand (ml bwa,ml -gcc,ml), andmodule spiderfor case-insensitive search across the whole tree — much better discovery thanmodule avail | grep.- User collections:
module save <name>/module restore <name>so users can snapshot a working toolset. Frequently requested; env-modules 5 has a weaker version of this. - Familiarity — most national HPC centers (TACC, CURC/Alpine, etc.) run Lmod, so new users and docs line up.
Wins that don't really matter here (yet):
- Hierarchical modules (compiler/MPI-dependent trees) — Bodhi's tree is flat and has no MPI stack, so the headline Lmod feature is unused. Worth adopting only if a toolchain hierarchy is introduced later.
- Module caching — a speed feature for trees with thousands of modules; at ~124
packages
module availis already instant.
Is it worth doing?
At this scale the case is ergonomics and ecosystem alignment, not necessity. env-modules 5.3.0 is perfectly serviceable. The migration is cheap and low-risk, so it's a reasonable "yes" — but it's a quality-of-life upgrade, not a fix for a broken system.
Migration plan¶
Lmod can run alongside env-modules and consume the existing Tcl tree read-only, so this is a low-stakes, reversible rollout.
Phase 0 — Prerequisites¶
Install Lmod and its Lua dependencies. Bodhi is RHEL 9 (el9); EPEL packages Lmod directly:
# On the login node + all compute nodes (or the shared /cluster image)
dnf install epel-release
dnf install Lmod # pulls lua, lua-posix, lua-filesystem, tcl
Alternatively build from source or via Spack if a specific Lmod version is
wanted. Confirm tclsh (8.6, present) is available on every node — Lmod shells
out to it to interpret Tcl modulefiles.
Warning
Install on every node, not just the login node. Compute nodes must
resolve module load inside batch jobs.
Phase 1 — Parallel install, no default change¶
- Configure Lmod's
MODULEPATHto point at the existing/cluster/software/modules-sw/modulefiles(andmodules-perl). No files are copied or rewritten. - Do not touch
/etc/profile.d/modules.sh. Env-modules stays the default. - Make Lmod opt-in for testing via an explicit init source, e.g.
source /usr/share/lmod/lmod/init/bash.
Phase 2 — Fix flagged files & clean up¶
Apply the four fixes from Issues to fix:
add the trimmomatic cookie, rewrite the gcloud exec, optionally convert
module load → depends-on, and drop the stale MODULEPATH entry. All are safe
under env-modules too, so they can land before the cutover.
Phase 3 — Smoke test every package¶
Validate that all ~124 packages load and that defaults resolve correctly under Lmod. A mechanical sweep:
source /usr/share/lmod/lmod/init/bash
export MODULEPATH=/cluster/software/modules-sw/modulefiles
fail=0
for m in $(module -t --redirect avail 2>/dev/null); do
if module load "$m" 2>err.log; then
module unload "$m" 2>/dev/null
else
echo "LOAD FAIL: $m"; cat err.log; fail=1
fi
done
echo "done (fail=$fail)"
# Confirm each package's *default* matches the old .modulerc default:
module -t --redirect avail | sort > /tmp/lmod-avail.txt
module -D --redirect avail 2>&1 | grep -i default # inspect (D)efault markers
Pay special attention to the 34 files with if/[…] logic and the 104
.modulerc defaults.
Phase 4 — Cutover¶
During a maintenance window (reservation):
- Swap the shell init so new logins get Lmod:
/etc/profile.d/modules.sh→ Lmod'sz00_lmod.sh(profile.d). - Keep env-modules installed for rollback — reverting is a one-line profile.d swap.
- Update the login splash / user docs to mention
mlandmodule spider. - Announce user-facing changes (below).
Phase 5 — Soak & decommission¶
After a soak period (e.g. one maintenance cycle) with no regressions, remove the env-modules profile.d hooks. Leave the package installed until confidence is high.
User-facing changes to announce¶
Almost everything is identical; the deltas worth a heads-up:
| Old (env-modules) | New (Lmod) | Notes |
|---|---|---|
module load bwa |
module load bwa or ml bwa |
unchanged; ml is the new shorthand |
module avail |
module avail / module spider bwa |
spider searches the whole tree |
module list |
ml (no args) |
shorthand |
| — | module save fqxv / module restore fqxv |
new: save/restore toolsets |
module load bcftools (→ default) |
same | validate the default resolves the same version |
.bashrc impact
User .bashrc files that call module load modules-init (as the reference
bashrc.example does) keep working — the module function is provided by
whichever system is active. No user .bashrc edits are required for the
cutover.
Rollback¶
Because Lmod never modifies the Tcl modulefiles and env-modules stays installed
through Phase 5, rollback at any point is: restore the original
/etc/profile.d/modules.sh and remove the Lmod profile.d snippet. New logins
revert immediately; no data or modulefile changes to undo.