Build Caches

To avoid recompilation of Spack packages, installed packages can be pushed to a build cache, and then downloaded and installed by others.

Whenever a mirror provides prebuilt packages, Spack will take these packages into account during concretization and installation, making spack install significantly faster.

Note

We use the terms “build cache” and “mirror” often interchangeably. Mirrors are used during installation for both sources and prebuilt packages. Build caches refer to mirrors that provide prebuilt packages.

Creating a Build Cache

Build caches are created via:

$ spack buildcache push <path/url/mirror name> <spec>

This command takes the locally installed spec and its dependencies, and creates tarballs of their install prefixes. It also generates metadata files, signed with GPG. These tarballs and metadata files are then pushed to the provided build cache, which can be a local directory or a remote URL.

Here is an example where a build cache is created in a local directory named “spack-cache”, to which we push the “ninja” spec:

$ spack buildcache push ./spack-cache ninja
==> Selected 30 specs to push to file:///home/spackuser/spack/spack-cache
...
==> [30/30] Pushed ninja@1.12.1/ngldn2k

Note that ninja must be installed locally for this to work.

Once you have a build cache, you can add it as a mirror, as discussed next.

Finding or Installing Build Cache Files

To find or install build cache files, a Spack mirror must be configured with:

$ spack mirror add <name> <url or path>

Both URLs and local paths on the filesystem can be specified. In the previous example, you might add the directory “spack-cache” and call it mymirror:

$ spack mirror add mymirror ./spack-cache

You can see that the mirror is added with spack mirror list as follows:

$ spack mirror list
mymirror           file:///home/spackuser/spack/spack-cache
spack-public       https://spack-llnl-mirror.s3-us-west-2.amazonaws.com/

At this point, you’ve created a build cache, but Spack hasn’t indexed it, so if you run spack buildcache list, you won’t see any results. You need to index this new build cache as follows:

$ spack buildcache update-index ./spack-cache

Now you can use list:

$ spack buildcache list
==> 24 cached builds.
-- linux-ubuntu22.04-sapphirerapids / gcc@12.3.0 ----------------
[ ... ]
ninja@1.12.1

With mymirror configured and an index available, Spack will automatically use it during concretization and installation. That means that you can expect spack install ninja to fetch prebuilt packages from the mirror. Let’s verify by reinstalling ninja:

$ spack uninstall ninja
$ spack install ninja
[ ... ]
==> Installing ninja-1.12.1-ngldn2kpvb6lqc44oqhhow7fzg7xu7lh [24/24]
gpg: Signature made Thu 06 Mar 2025 10:03:38 AM MST
gpg:                using RSA key 75BC0528114909C076E2607418010FFAD73C9B07
gpg: Good signature from "example (GPG created for Spack) <example@example.com>" [ultimate]
==> Fetching file:///home/spackuser/spack/spack-cache/blobs/sha256/f0/f08eb62661ad159d2d258890127fc6053f5302a2f490c1c7f7bd677721010ee0
==> Fetching file:///home/spackuser/spack/spack-cache/blobs/sha256/c7/c79ac6e40dfdd01ac499b020e52e57aa91151febaea3ad183f90c0f78b64a31a
==> Extracting ninja-1.12.1-ngldn2kpvb6lqc44oqhhow7fzg7xu7lh from binary cache
==> ninja: Successfully installed ninja-1.12.1-ngldn2kpvb6lqc44oqhhow7fzg7xu7lh
  Search: 0.00s.  Fetch: 0.11s.  Install: 0.11s.  Extract: 0.10s.  Relocate: 0.00s.  Total: 0.22s
[+] /home/spackuser/spack/opt/spack/linux-ubuntu22.04-sapphirerapids/gcc-12.3.0/ninja-1.12.1-ngldn2kpvb6lqc44oqhhow7fzg7xu7lh

It worked! You’ve just completed a full example of creating a build cache with a spec of interest, adding it as a mirror, updating its index, listing the contents, and finally, installing from it.

By default, Spack falls back to building from sources when the mirror is not available or when the package is simply not already available. To force Spack to install only prebuilt packages, you can use:

$ spack install --use-buildcache only <package>

For example, to combine all of the commands above to add the E4S build cache and then install from it exclusively, you would do:

$ spack mirror add E4S https://cache.e4s.io
$ spack buildcache keys --install --trust
$ spack install --use-buildcache only <package>

The --install and --trust flags install keys to the keyring and trust all downloaded keys.

Build Cache Index Views

Note

Introduced in Spack v1.2. The addition of this feature does not increment the build cache version (v3).

Note

Build cache index views are not supported in OCI build caches.

Build caches can quickly become large and inefficient to search as binaries are added over time. A common work around to this problem is to break the build cache into stacks that target specific applications or workflows. This allows for curation of binaries as smaller collections of packages that push to their own mirrors that each maintain a smaller search area. However, this approach comes with the trade off of requiring much larger storage and computational footprints due to duplication of common dependencies between stacks. Splitting build caches can also reduce direct fetch hits by reducing the breadth of binaries available in a single mirror.

To better address the issues with large search areas, build cache index views (or just “views” in this section) were introduced. A view is a named index which provides a curated view into a larger build cache. This allows build cache maintainers to provide the same granularity of build caches split by stacks without having to pay for the extra storage and compute required for the duplicated dependencies.

Views can be created or updated using an active environment, or a list of environment names or paths. The spack buildcache commands for views are alias of the command spack buildcache update-index.

View indices are stored similarly to the top level build cache index, but use an additional prefix of the view name <build cache prefix>/v3/manifests/index/my-stack/index.manifest.json.

Creating a Build Cache Index View

Here is an example of creating a view using an active environment.

$ spack env activate my-stack
$ spack install
$ spack buildcache push my-mirror
$ spack buildcache update-index --name my-view my-mirror

It is also possible to create a view from a list of one or more environments by passing the environment names or paths. If a list of environments is passed while inside of an active environment, the active environment is ignored and only the passed environments are considered.

$ spack buildcache update-index --name my-view my-mirror my-stack /path/to/environment/my-other-stack

Updating a Build Cache Index View

To prevent accidentally overwriting an existing view, it is required to specify how a view should be updated. It is possible to use one of two options for updating a view index: --force or --append. Using the --force option will replace the index as if the previous one did not exist. The --append option will first read the existing index, and then add the new specs to it.

$ spack buildcache push my-mirror
$ spack buildcache update-index --append --name my-view my-mirror my-stack

Warning

Using the --append option with build cache index views is a non-atomic operation. In the case where multiple writers are appending to the same view, the result will only include the state of the last to write. When using --append for build cache workflows it is up to the user to correctly serialize the update operations.

Creating and Trusting GPG keys

spack gpg

Spack has support for signing and verifying packages using GPG keys. A separate keyring is used for Spack, so any keys available in the user’s home directory are not used.

spack gpg init

When Spack is first installed, its keyring is empty. Keys stored in var/spack/gpg are the default keys for a Spack installation. These keys may be imported by running spack gpg init. This will import the default keys into the keyring as trusted keys.

Trusting keys

Additional keys may be added to the keyring using:

$ spack gpg trust <keyfile>

Once a key is trusted, packages signed by the owner of the key may be installed.

To remove keys from your keyring, use:

$ spack gpg untrust <keyid>

Key IDs can be email addresses, names, or (preferably) fingerprints.

Creating keys

You may also create your own key so that you may sign your own packages using

$ spack gpg create <name> <email>

By default, the key has no expiration, but it may be set with the --expires <date> flag. It is also recommended to add a comment as to the use of the key using the --comment <comment> flag. The public half of the key can also be exported for sharing with others so that they may use packages you have signed using the --export <keyfile> flag. Secret keys may also be later exported using the spack gpg export <location> [<key>...] command.

Key creation speed

The creation of a new GPG key requires generating a lot of random numbers. Depending on the entropy produced on your system, the entire process may take a long time (and may even appear to hang). Virtual machines and cloud instances are particularly likely to display this behavior.

To speed it up, you may install tools like rngd, which is usually available as a package in the host OS. Another alternative is haveged, which can be installed on RHEL/CentOS machines.

This Digital Ocean tutorial provides a good overview of sources of randomness.

Build Cache Signing

By default, Spack will add a cryptographic signature to each package pushed to a build cache and verify the signature when installing from a build cache.

Keys for signing can be managed with the spack gpg command, as well as spack buildcache keys, as mentioned above.

You can disable signing when pushing with spack buildcache push --unsigned and disable verification when installing from any build cache with spack install --no-check-signature.

Alternatively, signing and verification can be enabled or disabled on a per-build-cache basis:

$ spack mirror add --signed <name> <url>  # enable signing and verification
$ spack mirror add --unsigned <name> <url>  # disable signing and verification

$ spack mirror set --signed <name>  # enable signing and verification for an existing mirror
$ spack mirror set --unsigned <name>  # disable signing and verification for an existing mirror

Alternatively, you can edit the mirrors.yaml configuration file directly:

mirrors:
  <name>:
    url: <url>
    signed: false # disable signing and verification

See also Mirrors (mirrors.yaml).

Relocation

When using build caches across different machines, it is likely that the install root is different from the one used to build the binaries.

To address this issue, Spack automatically relocates all paths encoded in binaries and scripts to their new location upon installation.

Note that there are some cases where this is not possible: if binaries are built in a relatively short path and then installed to a longer path, there may not be enough space in the binary to encode the new path. In this case, Spack will fail to install the package from the build cache, and a source build is required.

To reduce the likelihood of this happening, it is highly recommended to add padding to the install root during the build, as specified in the config section of the configuration:

config:
  install_tree:
    root: /opt/spack
    padded_length: 128

Automatic Push to a Build Cache

Sometimes it is convenient to push packages to a build cache immediately after they are installed. Spack can do this by setting the --autopush flag when adding a mirror:

$ spack mirror add --autopush <name> <url or path>

Or the --autopush flag can be set for an existing mirror:

$ spack mirror set --autopush <name>  # enable automatic push for an existing mirror
$ spack mirror set --no-autopush <name>  # disable automatic push for an existing mirror

Then, after installing a package, it is automatically pushed to all mirrors with autopush: true. The command

$ spack install <package>

will have the same effect as

$ spack install <package>
$ spack buildcache push <cache> <package>  # for all caches with autopush: true

Note

Packages are automatically pushed to a build cache only if they are built from source.

OCI / Docker V2 Registries as Build Cache

Spack can also use OCI or Docker V2 registries such as Docker Hub, Quay.io, Amazon ECR, GitHub Packages, GitLab Container Registry, JFrog Artifactory, and others as build caches. This is a convenient way to share binaries using public infrastructure or to cache Spack-built binaries in GitHub Actions and GitLab CI. These registries can be used not only to share Spack binaries but also to create and distribute runnable container images.

To get started, configure an OCI mirror using oci:// as the scheme and optionally specify variables that hold the username and password (or personal access token) for the registry:

$ spack mirror add --oci-username-variable REGISTRY_USER \
                   --oci-password-variable REGISTRY_TOKEN \
                   my_registry oci://example.com/my_image

This registers a mirror in your mirrors.yaml configuration file that looks as follows:

mirrors:
  my_registry:
    url: oci://example.com/my_image
    access_pair:
      id_variable: REGISTRY_USER
      secret_variable: REGISTRY_TOKEN

Spack follows the naming conventions of Docker, with Docker Hub as the default registry. To use Docker Hub, you can omit the registry domain:

$ spack mirror add ... my_registry oci://username/my_image

From here, you can use the mirror as any other build cache:

$ export REGISTRY_USER=...
$ export REGISTRY_TOKEN=...
$ spack buildcache push my_registry <specs...>  # push to the registry
$ spack install <specs...>  # or install from the registry

Note

Spack defaults to https for OCI registries, and does not fall back to http in case of failure. For local registries which use http instead of https, you can specify oci+http://localhost:5000/my_image.

Build Cache and Container Images

A unique feature of build caches on top of OCI registries is that it’s incredibly easy to generate a runnable container image with the binaries installed. This is a great way to make applications available to users without requiring them to install Spack – all you need is Docker, Podman, or any other OCI-compatible container runtime.

To produce container images, all you need to do is add the --base-image flag when pushing to the build cache:

$ spack buildcache push --base-image ubuntu:20.04 my_registry ninja
Pushed to example.com/my_image:ninja-1.11.1-yxferyhmrjkosgta5ei6b4lqf6bxbscz.spack

$ docker run -it example.com/my_image:ninja-1.11.1-yxferyhmrjkosgta5ei6b4lqf6bxbscz.spack
root@e4c2b6f6b3f4:/# ninja --version
1.11.1

If --base-image is not specified, Spack produces distroless images. In practice, you won’t be able to run these as containers because they don’t come with libc and other system dependencies. However, they are still compatible with tools like skopeo, podman, and docker for pulling and pushing.

See the section Exporting Spack installations as Container Images for more details on how to create container images with Spack.

Spack Build Cache for GitHub Actions

To significantly speed up Spack in GitHub Actions, binaries can be cached in GitHub Packages. This service is an OCI registry that can be linked to a GitHub repository.

Spack offers a public build cache for GitHub Actions with a set of common packages, which lets you get started quickly. See the following resources for more information:

spack buildcache

spack buildcache push

Create a tarball of an installed Spack package and all its dependencies. Tarballs and specfiles are compressed and checksummed; manifests are signed if GPG2 is available. Commands like spack buildcache install will search Spack mirrors to get the list of build caches.

Arguments

Description

<specs>

list of partial specs or hashes with a leading / to match from installed packages and used for creating build caches

-d <path>

directory in which v3 and blobs directories are created, defaults to .

-f

overwrite compressed tarball and spec metadata files if they already exist

-k <key>

the key to sign package with. In the case where multiple keys exist, the package will be unsigned unless -k is used.

-r

make paths in binaries relative before creating tarball

-y

answer yes to all questions about creating unsigned build caches

spack buildcache list

Retrieves all specs for build caches available on a Spack mirror.

Arguments

Description

<specs>

list of partial package specs to be matched against specs downloaded for build caches

E.g., spack buildcache list gcc will print only commands to install gcc package(s).

spack buildcache install

Retrieves all specs for build caches available on a Spack mirror and installs build caches with specs matching the input specs.

Arguments

Description

<specs>

list of partial package specs or hashes with a leading / to be installed from build caches

-f

remove install directory if it exists before unpacking tarball

-y

answer yes to all to don’t verify package with gpg questions

spack buildcache keys

List public keys available on a Spack mirror.

Arguments

Description

-it

trust the keys downloaded with prompt for each

-y

answer yes to all trust all keys downloaded

Build Cache Layout

This section describes the structure and content of URL-style build caches, as distinguished from OCI-style build caches.

The entry point for a binary package is a manifest JSON file that references at least two other files stored as content-addressed blobs. These files include a spec metadata file, as well as the installation directory of the package stored as a compressed archive file. Binary package manifest files are named to indicate the package name and version, as well as the hash of the concrete spec. For example:

gcc-runtime-12.3.0-qyu2lvgt3nxh7izxycugdbgf5gsdpkjt.spec.manifest.json

would contain the manifest for a binary package of gcc-runtime@12.3.0. The ID of the built package is defined to be the DAG hash of the concrete spec and exists in the name of the file as well. The ID distinguishes a particular binary package from all other binary packages with the same package name and version. Below is an example binary package manifest file. Such a file would live in the versioned spec manifests directory of a binary mirror, for example, v3/manifests/spec/:

{
  "version": 3,
  "data": [
    {
      "contentLength": 10731083,
      "mediaType": "application/vnd.spack.install.v2.tar+gzip",
      "compression": "gzip",
      "checksumAlgorithm": "sha256",
      "checksum": "0f24aa6b5dd7150067349865217acd3f6a383083f9eca111d2d2fed726c88210"
    },
    {
      "contentLength": 1000,
      "mediaType": "application/vnd.spack.spec.v5+json",
      "compression": "gzip",
      "checksumAlgorithm": "sha256",
      "checksum": "fba751c4796536737c9acbb718dad7429be1fa485f5585d450ab8b25d12ae041"
    }
  ]
}

The manifest references both the compressed tar file as well as the compressed spec metadata file, and contains the checksum of each. This checksum is also used as the address of the associated file and, hence, must be known in order to locate the tarball or spec file within the mirror. Once the tarball or spec metadata file is downloaded, the checksum should be computed locally and compared to the checksum in the manifest to ensure the contents have not changed since the binary package was pushed. Spack stores all data files (including compressed tar files, spec metadata, indices, public keys, etc.) within a blobs/<hash-algorithm>/ directory, using the first two characters of the checksum as a subdirectory to reduce the number of files in a single folder. Here is a depiction of the organization of binary mirror contents:

mirror_directory/
  v3/
    layout.json
    manifests/
      spec/
        gcc-runtime/
          gcc-runtime-12.3.0-s2nqujezsce4x6uhtvxscu7jhewqzztx.spec.manifest.json
        gmake/
          gmake-4.4.1-lpr4j77rcgkg5536tmiuzwzlcjsiomph.spec.manifest.json
        compiler-wrapper/
          compiler-wrapper-1.0-s7ieuyievp57vwhthczhaq2ogowf3ohe.spec.manifest.json
      index/
        index.manifest.json
      key/
        75BC0528114909C076E2607418010FFAD73C9B07.key.manifest.json
        keys.manifest.json
  blobs/
    sha256/
      0f/
        0f24aa6b5dd7150067349865217acd3f6a383083f9eca111d2d2fed726c88210
      fb/
        fba751c4796536737c9acbb718dad7429be1fa485f5585d450ab8b25d12ae041
      2a/
        2a21836d206ccf0df780ab0be63fdf76d24501375306a35daa6683c409b7922f
      ...

Files within the manifests directory are organized into subdirectories by the type of entity they represent. Binary package manifests live in the spec/ directory, build cache index manifests live in the index/ directory, and manifests for public keys and their indices live in the key/ subdirectory. Regardless of the type of entity they represent, all manifest files are named with an extension .manifest.json.

Every manifest contains a data array, each element of which refers to an associated file stored as a content-addressed blob. Considering the example spec manifest shown above, the compressed installation archive can be found by picking out the data blob with the appropriate mediaType, which in this case would be application/vnd.spack.install.v2.tar+gzip. The associated file is found by looking in the blobs directory under blobs/sha256/fb/ for the file named with the complete checksum value.

As mentioned above, every entity in a build cache is stored as a content-addressed blob pointed to by a manifest. While an example spec manifest (i.e., a manifest for a binary package) is shown above, here is what the manifest of a build cache index looks like:

{
  "version": 3,
  "data": [
    {
      "contentLength": 6411,
      "mediaType": "application/vnd.spack.db.v8+json",
      "compression": "none",
      "checksumAlgorithm": "sha256",
      "checksum": "225a3e9da24d201fdf9d8247d66217f5b3f4d0fc160db1498afd998bfd115234"
    }
  ]
}

Some things to note about this manifest are that it points to a blob that is not compressed (compression: "none") and that the mediaType is one we have not seen yet, application/vnd.spack.db.v8+json. The decision not to compress build cache indices stems from the fact that Spack does not yet sign build cache index manifests. Once that changes, you may start to see these indices stored as compressed blobs.

For completeness, here are examples of manifests for the other two types of entities you might find in a Spack build cache. First, a public key manifest:

{
  "version": 3,
  "data": [
    {
      "contentLength": 2472,
      "mediaType": "application/pgp-keys",
      "compression": "none",
      "checksumAlgorithm": "sha256",
      "checksum": "9fc18374aebc84deb2f27898da77d4d4410e5fb44c60c6238cb57fb36147e5c7"
    }
  ]
}

Note the mediaType of application/pgp-keys. Finally, a public key index manifest:

{
  "version": 3,
  "data": [
    {
      "contentLength": 56,
      "mediaType": "application/vnd.spack.keyindex.v1+json",
      "compression": "none",
      "checksumAlgorithm": "sha256",
      "checksum": "29b3a0eb6064fd588543bc43ac7d42d708a69058dafe4be0859e3200091a9a1c"
    }
  ]
}

Again, note the mediaType of application/vnd.spack.keyindex.v1+json. Also, note that both the above manifest examples refer to uncompressed blobs; this is for the same reason Spack does not yet compress build cache index blobs.