Spack Concrete Specs: Pinning Versions, Compilers and Build Options for Reproducible Builds
A concrete Spack spec pins exact versions, compilers, and build options into a fully resolved dependency graph. Learn how concretization works, how to verify a pinned spec, and common failure modes.
26 Feb 2026, 01:47 UTC

If you need the same binary layout on two machines, the useful answer is to stop installing from an abstract spec and commit a concrete spec. A concrete spec is Spack's fully resolved dependency graph with exact package versions, compilers, variants and hashes pinned. Once concretized, the same spec always maps to the same build.
What a concrete spec locks down
An abstract spec describes intent: hdf5 ^zlib %gcc. It leaves version, compiler version, and variant choices open. Concretization resolves those choices against the package registry, your installed compilers, platform, and existing constraints.
A concrete spec pins:
- package name and exact version
- compiler name and version
- variants and build options
- dependencies with their own pins
The result is a deterministic install plan. Spack stores it in the database and reuses it for spack install.
How concretization resolves a spec
Concretization evaluates the user spec, consults the package registry, and applies compiler, platform and constraint rules to select a mutually compatible set. Spack's concretizer is a constraint solver. It picks versions that satisfy dependencies, then chooses a compiler that satisfies all packages, and finally fixes variants.
Worked example. You want HDF5 1.14.3 built with GCC 13.2.0 and a specific zlib.
# Run in a shell where spack is initialized. No elevated permissions needed.
spack concretize hdf5@1.14.3 ^zlib@1.3.1 %gcc@13.2.0
The command is read‑only. It prints a concrete spec without building. For a project, pin the intent in spack.yaml:
spack:
specs:
- hdf5@1.14.3 ^zlib@1.3.1 %gcc@13.2.0
concretization: together
Then run:
spack concretize -f spack.yaml
Expected checks: the output contains a fully specified node for hdf5 with version, compiler, and a hash, and a child node for zlib with its own pins. If you save the spec with spack spec --concrete --json you get a machine‑readable record you can commit.
Reading and verifying the result
Verify reproducibility by inspecting the concrete spec:
spack concretize hdf5@1.14.3 ^zlib@1.3.1 %gcc@13.2.0 --format yaml
Look for pinned fields: version, compiler, variants, and hash. A concrete spec will show no wildcards.
Compare across runs. With identical inputs, the same spec should produce the same compiler and version choices and the same content hash. Differences indicate a change in the package repository, compiler availability, or environment.
Limits, failures and common mistakes
Concretization fails when no compatible set exists. Common causes:
- Conflicting compiler requirements. One dependency requires
%gcc@12:and another requires%clang@17:. Resolution requires explicit compiler selection or relaxing a version constraint. - Platform mismatch. A package variant requires a library unavailable on your OS.
- Overly broad specs.
hdf5 ^zlibwithout versions forces the solver to explore many combinations, increasing time and risk of non‑determinism.
Large dependency graphs cause long concretization times because the solver explores combinations exhaustively. Mitigate by narrowing constraints and pinning key versions.
Common mistake: assuming spack install will always pick the same compiler. It uses the environment's default unless you specify %compiler in the spec or set spack config defaults. Pin the compiler in the spec for reproducibility.
Practical habits for stable concretization
- Specify compiler and major versions explicitly for top‑level packages.
- Use
spack concretizebefore install to review the graph without triggering builds. - Commit the concrete spec or lockfile for projects. Re‑run concretization only after intentional changes.
- When a conflict appears, relax the least critical constraint first, then re‑concretize.
Concrete specs are the mechanism Spack uses to turn intent into a fixed, repeatable build. Pinning versions, compilers and variants, and verifying the YAML output is the practical way to keep environments identical.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.