Using Spack Concretization to Achieve Reproducible HPC Builds
Learn how Spack’s concretization turns abstract package specs into hash‑identified build DAGs, ensuring identical software across diverse HPC clusters.
08 Oct 2025, 12:38 UTC

The problem: version drift on heterogeneous clusters
When you install software on different HPC systems, subtle differences in compilers, libraries, or target architectures can cause the same abstract specification to resolve to different builds. This leads to broken dependencies, non‑reproducible results, and time spent debugging mismatched stacks.
Thesis: Spack’s concretization step locks down an abstract spec into a concrete, hash‑identified DAG
Spack separates the user‑facing abstract specification (what you want) from the concrete build DAG (what actually gets built). The concretization process resolves variants, dependencies, and compiler choices, then assigns a stable hash that uniquely identifies the resulting DAG. If two runs produce the same hash, the builds are bit‑for‑bit identical, regardless of the underlying system.
Worked example: concretizing HDF5 with OpenMPI on two architectures
- Where to run: On a login node of each cluster, using your regular user account (no special privileges required).
- Step 1 – view the abstract DAG:
This prints a dependency graph with abstract nodes (e.g.,spack spec -l hdf5 ^openmpi %gcc@11.2.0hdf5,openmpi,gcc) but no hashes. - Step 2 – concretize and capture the hash:
Thespack concretize -f hdf5 ^openmpi %gcc@11.2.0-fflag forces concretization and outputs the concrete spec, including a hash likeabcd1234. Note the hash; you will compare it across systems. - Step 3 – verify architecture‑dependent differences:
Run this on each system to see the target architecture (e.g.,spack arch -plinux-rhel8-skylake_avx512vslinux-rhel8-zen2). Then re‑run the concretization command and observe that the hash changes only in the nodes tied to the architecture (such as the binary package foropenmpior the compiler wrapper). - Step 4 – trace hash contributors (optional):
This shows which DAG nodes contributed to the final hash, confirming that the difference stems from architecture‑specific packages.spack blame -d <concrete-spec>
Expected checks: After concretization, you should see a hash printed. Changing the target architecture (via spack arch or a different config.yaml) should produce a different hash, but the abstract spec remains unchanged. If the hash stays identical across architectures, verify that you are not inadvertently forcing a universal variant (e.g., %gcc that is available on both).
Risks: Concretization can be slow for large dependency graphs because Spack explores many possible configurations. Over‑specifying contradictory variants (e.g., requesting both +mpi and ~mpi) will cause concretization to fail with an error about unsatisfiable constraints.
Trade‑off and practical tips
- Speed vs. certainty: For routine workflows, run concretization once, store the concrete spec (e.g., in a
spack.yamlor lock file), and reuse it. This avoids repeated solving. - Managing variants: Keep abstract specs flexible. Instead of hard‑coding
+mpi, allow Spack to choose based on dependencies, and only override when you truly need a non‑default setting. - Repository freshness: Concretization results depend on the current package repository. Update Spack’s package index (
spack repo update) regularly to avoid drifting hashes due to outdated package recipes.
Actionable closing
To make your HPC builds reproducible, adopt the habit of concretizing your abstract specs and locking the resulting hash in a project‑level spack.lock or spack.yaml. Verify the hash on each target architecture with the steps above, and update the lock file only when you intentionally change a dependency or compiler version. This practice eliminates version drift and gives you confidence that the same binary will run everywhere.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.