Package Repositories (repos.yaml)

Spack comes with thousands of built-in package recipes. As of Spack v1.0, these are hosted in a separate Git repository at spack/spack-packages.

A package repository is a directory that Spack searches when it needs to find a package by name. You may need to maintain packages for restricted, proprietary, or experimental software separately from the built-in repository. Spack allows you to configure local and remote repositories using either the repos.yaml configuration file or the spack repo command.

This document describes how to set up and manage these package repositories.

Structure of an Individual Package Repository

An individual Spack package repository is a directory structured as follows:

/path/to/repos/                   # the top-level dir is added to the Python search path
  spack_repo/                     # every package repository is part of the spack_repo Python module
    myrepo/                       # directory for the 'myrepo' repository (matches namespace)
      repo.yaml                   # configuration file for this package repository
      packages/                   # directory containing package directories
        hdf5/                     # directory for the hdf5 package
          package.py              # the package recipe file
        mpich/                    # directory for the mpich package
          package.py              # the package recipe file
          mpich-1.9-bugfix.patch  # example patch file
        trilinos/
          package.py
    ...
  • repo.yaml. This file contains metadata for this specific repository, for example:

    repo:
      namespace: myrepo
      api: v2.0
    

    It defines primarily:

    • namespace. A unique identifier for this repository (e.g., myrepo, projectx). See the Namespaces section for more details.

    • api. The version of the Spack Package API this repository adheres to (e.g., v2.0). Spack itself defines what range of API versions it supports, and will error if it encounters a repository with an unsupported API version.

  • packages/. This directory contains subdirectories for each package in the repository. Each package directory contains a package.py file and any patches or other files needed to build the package.

Package repositories allow you to:

  1. Maintain your own packages separately from Spack’s built-in set.

  2. Share your packages (e.g., by hosting them on a shared file system or in a Git repository) without committing them to the main spack/spack-packages repository.

  3. Override built-in Spack packages with your own implementations.

Packages in a separate repository can also depend on built-in Spack packages, allowing you to leverage existing recipes without re-implementing them.

Package Names

Package names are defined by the directory names under packages/. In the example above, the package names are hdf5, mpich, and trilinos. Package names can only contain lowercase characters a-z, digits 0-9 and hyphens -.

Note

Package names are derived from the directory names under packages/. Package directories are required to be valid Python module names, which means they cannot contain hyphens or start with a digit. This means that a package named my-package would be stored in a directory named my_package/, and a package named 7zip would be stored in a directory named _7zip/ with an underscore prefix to make it a valid Python module name. The mapping between package names and directory names is one-to-one. Use spack list to see how Spack resolves the package names from the directory names.

Configuring Repositories with repos.yaml

Spack uses repos.yaml files found in its configuration scopes (e.g., ~/.spack/, etc/spack/) to discover and prioritize package repositories. Note that this repos.yaml (plural) configuration file is distinct from the repo.yaml (singular) file within each individual package repository.

Spack supports two main types of repository configurations:

Local Repositories (Path-based)

You can point Spack to a repository on your local filesystem:

~/.spack/repos.yaml
repos:
  my_local_packages: /path/to/my_repository_root

Here, /path/to/my_repository_root should be the directory containing that repository’s repo.yaml and packages/ subdirectory.

Git-based Repositories

Spack can clone and use repositories directly from Git URLs:

repos:
  my_remote_repo: https://github.com/myorg/spack-custom-pkgs.git

Automatic Cloning

When Spack first encounters a Git-based repository configuration, it automatically clones it. By default, these repositories are cloned into a subdirectory within ~/.spack/package_repos/, named with a hash of the repository URL.

To change directories to the package repository, you can use spack cd --repo [name]. To find where a repository is cloned, you can use spack location --repo [name] or spack repo list. The name argument is optional; if omitted, Spack will use the first package repository in configuration order.

Customizing Clone Location

The default clone location (~/.spack/package_repos/<hashed_name>) might not be convenient for package maintainers who want to make changes to packages. You can specify a custom local directory for Spack to clone a Git repository into, or to use if the repository is already cloned there. This is done using the destination key in repos.yaml or via the spack repo set --destination command (see spack repo set).

For example, to use ~/custom_packages_clone for my_remote_repo:

~/.spack/repos.yaml
repos:
  my_remote_repo:
    git: https://github.com/myorg/spack-custom-pkgs.git
    destination: ~/custom_packages_clone

If the git URL is defined in a lower-precedence configuration (like Spack’s defaults for builtin), you only need to specify the destination in your user-level repos.yaml. Spack can make the configuration changes for you using spack repo set --destination ~/spack-packages builtin, or you can directly edit your repos.yaml file:

~/.spack/repos.yaml
repos:
  builtin:
    destination: ~/spack-packages

Updating and pinning

Repos can be pinned to a git branch, tag, or commit.

~/.spack/repos.yaml
repos:
  builtin:
    branch: releases/v2025.07
    # tag: v2025.07.0
    # commit: 6427933daecef74b981d1f773731aeace3b06ede

The spack repo update command will update the repo on disk to match the current state of the config. If the repo is pinned to a commit or tag, it will ensure the repo on disk reflects that commit or tag. If the repo is pinned to a branch or unpinned, spack repo update will pull the most recent state of the branch (the default branch if unpinned).

Git repositories need a package repo index

A single Git repository can contain one or more Spack package repositories. To enable Spack to discover these, the root of the Git repository should contain a spack-repo-index.yaml file. This file lists the relative paths to package repository roots within the git repo.

For example, assume a Git repository at https://example.com/my_org/my_pkgs.git has the following structure

my_pkgs.git/
  spack-repo-index.yaml     # metadata file at the root of the Git repo
  ...
  spack_pkgs/
    spack_repo/
      my_org/
        comp_sci_packages/  # package repository for computer science packages
          repo.yaml
          packages/
            hdf5/
              package.py
            mpich/
              package.py
        physics_packages/   # package repository for physics packages
          repo.yaml
          packages/
            gromacs/
              package.py

The spack-repo-index.yaml in the root of https://example.com/my_org/my_pkgs.git should look like this:

my_pkgs.git/spack-repo-index.yaml
repo_index:
  paths:
  - spack_pkgs/spack_repo/my_org/comp_sci_packages
  - spack_pkgs/spack_repo/my_org/physics_packages

If my_pkgs.git is configured in repos.yaml as follows:

~/.spack/repos.yaml
repos:
  example_mono_repo: https://example.com/my_org/my_pkgs.git

Spack will clone my_pkgs.git and look for spack-repo-index.yaml. It will then register two separate repositories based on the paths found (e.g., <clone_dir>/spack_pkgs/spack_repo/my_org/comp_sci_packages and <clone_dir>/spack_pkgs/spack_repo/my_org/physics_packages), each with its own namespace defined in its respective repo.yaml file. Thus, one repos.yaml entry for a Git mono-repo can lead to multiple repositories being available to Spack.

If you want only one of the package repositories from a Git mono-repo, you can override the paths in your user-level repos.yaml. For example, if you only want the computer science packages:

~/.spack/repos.yaml
repos:
  example_mono_repo:
    git: https://example.com/my_org/my_pkgs.git
    paths:
    - spack_pkgs/spack_repo/my_org/comp_sci_packages

The spack repo add command can help you set up these configurations easily.

The builtin Repository

Spack’s extensive collection of built-in packages resides at spack/spack-packages. By default, Spack is configured to use this as a Git-based repository. The default configuration in $spack/etc/spack/defaults/repos.yaml looks something like this:

repos:
  builtin:
    git: https://github.com/spack/spack-packages.git

Namespaces

Every repository in Spack has an associated namespace defined in the namespace: key of its top-level repo.yaml file. For example, the built-in repository (from spack/spack-packages) has its namespace defined as builtin:

repo.yaml of spack/spack-packages
repo:
  namespace: builtin
  api: v2.0 # Or newer

Spack records the repository namespace of each installed package. For example, if you install the mpich package from the builtin repo, Spack records its fully qualified name as builtin.mpich. This accomplishes two things:

  1. You can have packages with the same name from different namespaces installed simultaneously.

  2. You can easily determine which repository a package came from after it is installed (more below).

Note

The namespace defined in the package repository’s repo.yaml is the authoritative source for the namespace. It is not derived from the local configuration in repos.yaml. This means that the namespace is determined by the repository maintainer, not by the user or local configuration.

Nested Namespaces for Organizations

As we have already seen in the Git-based package repositories example above, you can create nested namespaces by using periods in the namespace name. For example, a repository for packages related to computation at LLNL might have the namespace llnl.comp, while one for physical and life sciences could be llnl.pls. On the file system, this requires a directory structure like this:

/path/to/repos/
  spack_repo/
    llnl/
      comp/
        repo.yaml  # Contains namespace: llnl.comp
        packages/
          mpich/
            package.py
      pls/
        repo.yaml  # Contains namespace: llnl.pls
        packages/
          hdf5/
            package.py

Uniqueness

Spack cannot ensure global uniqueness of all namespaces, but it will prevent you from registering two repositories with the same namespace at the same time in your current configuration. If you try to add a repository that has the same namespace as an already registered one, Spack will print a warning and may ignore the new addition or apply specific override logic depending on the configuration.

Namespace Example

Suppose LLNL maintains its own version of mpich (in a repository with namespace llnl.comp), separate from Spack’s built-in mpich package (namespace builtin). If you’ve installed both, spack find alone might be ambiguous:

$ spack find
==> 2 installed packages.
-- linux-rhel6-x86_64 / gcc@4.4.7 -------------
mpich@3.2  mpich@3.2

Using spack find -N displays packages with their namespaces:

$ spack find -N
==> 2 installed packages.
-- linux-rhel6-x86_64 / gcc@4.4.7 -------------
builtin.mpich@3.2  llnl.comp.mpich@3.2

Now you can distinguish them. Packages differing only by namespace will have different hashes:

$ spack find -lN
==> 2 installed packages.
-- linux-rhel6-x86_64 / gcc@4.4.7 -------------
c35p3gc builtin.mpich@3.2  itoqmox llnl.comp.mpich@3.2

All Spack commands that take a package spec also accept a fully qualified spec with a namespace, allowing you to be specific:

$ spack uninstall llnl.comp.mpich

Search Order and Overriding Packages

When Spack resolves an unqualified package name (e.g., mpich in spack install mpich), it searches the configured repositories in the order they appear in the merged repos.yaml configuration (from highest to lowest precedence scope, and top to bottom within each file). The first repository found that provides the package will be used. For Git-based mono-repos, the individual repositories listed in its spack-repo-index.yaml are effectively inserted into this search order based on the mono-repo’s position.

This search order allows you to override built-in packages. If you have your own mpich in a repository my_custom_repo, and my_custom_repo is listed before builtin in your repos.yaml, Spack will use your version of mpich by default.

Suppose your effective (merged) repos.yaml implies the following order:

  1. proto (local repo at ~/my_spack_repos/spack_repo/proto_repo)

  2. llnl (local repo at /usr/local/repos/spack_repo/llnl_repo)

  3. builtin (Spack’s default packages from spack/spack-packages)

And the packages are:

Namespace

Source

Packages

proto

~/my_spack_repos/spack_repo/proto_repo

mpich

llnl

/usr/local/repos/spack_repo/llnl_repo

hdf5

builtin

spack/spack-packages (Git)

mpich, hdf5, others

If hdf5 depends on mpich:

  • spack install hdf5 will install llnl.hdf5 ^proto.mpich. Spack finds hdf5 first in llnl. For its dependency mpich, Spack searches again from the top, finding mpich first in proto.

You can force a particular repository’s package using a fully qualified name:

  • spack install hdf5 ^builtin.mpich will install llnl.hdf5 ^builtin.mpich.

  • spack install builtin.hdf5 ^builtin.mpich will install builtin.hdf5 ^builtin.mpich.

To see which repositories will be used for a build before installing, use spack spec -N:

$ spack spec -N hdf5
llnl.hdf5@1.10.0
    ^proto.mpich@3.2
    ^builtin.zlib@1.2.8

Warning

While you can use a fully qualified package name in a depends_on directive within a package.py file (e.g., depends_on("proto.hdf5")), this is strongly discouraged. It makes the package non-portable and tightly coupled to a specific repository configuration, hindering sharing and composition of repositories. A package will fail to load if the hardcoded namespace’s repository is not registered.

The spack repo Command

Spack provides commands to manage your repository configurations.

spack repo list

This command shows all repositories Spack currently knows about, including their namespace, API version, and resolved path (local path or clone directory for Git repos).

$ spack repo list
[+] my_local           v2.0    /path/to/spack_repo/my_local_packages
[+] comp_sci_packages  v2.0    ~/.spack/package_repos/<hash 1>/spack_pkgs/spack_repo/comp_sci_packages
[+] physics_packages   v2.0    ~/.spack/package_repos/<hash 1>/spack_pkgs/spack_repo/physics_packages  # From the same git repo
[+] builtin            v2.0    ~/.spack/package_repos/<hash 2>/repos/spack_repo/builtin

Spack shows a green [+] next to each repository that is available for use. It shows a red [-] to indicate that package repositories cannot be used due to an error (e.g., unsupported API version, missing repo.yaml, etc.). It can also show just a gray - if it is a Git-based package repository that has not been cloned yet.

Note that for Git-based package repositories, spack repo list will show entries for each individual package repository registered via spack-repo-index.yaml. This contrasts with spack config get repos, which shows the raw configuration from repos.yaml files, including just the Git URL for a mono-repo:

$ spack config get repos
repos:
  my_local_packages: /path/to/spack_repo/my_local_packages
  example_mono_repo: https://example.com/my_org/my_pkgs.git # contains two package repositories
  builtin:
    git: https://github.com/spack/spack-packages.git
    # destination: /my/custom/path (if set by user)

spack repo create

To create the directory structure for a new, empty local repository:

$ spack repo create ~/my_spack_projects myorg.projectx
==> Created repo with namespace 'myorg.projectx'.
==> To register it with spack, run this command:
  spack repo add ~/my_spack_projects/spack_repo/myorg/projectx

This command creates the following structure:

~/my_spack_projects/
  spack_repo/
    myorg/
      projectx/
        repo.yaml      # Contains namespace: myorg.projectx
        packages/      # Empty directory for new package.py files

The <target_dir> is where the spack_repo/<namespace_parts> hierarchy will be created. The <namespace> can be simple (e.g., myrepo) or nested (e.g., myorg.projectx), and Spack will create the corresponding directory structure.

spack repo add

To register package repositories from local paths or a remote Git repositories with Spack:

  • For a local path: Provide the path to the repository’s root directory (the one containing repo.yaml and packages/).

    $ spack repo add ~/my_spack_projects/spack_repo/myorg/projectx
    ==> Added repo to config with name 'myorg.projectx'.
    
  • For a Git repository: Provide the Git URL.

    $ spack repo add --name my_pkgs https://github.com/spack/spack-packages.git ~/my_pkgs
    Cloning into '/home/user/my_pkgs'...
    ==> Added repo to config with name 'my_pkgs'.
    

    Notice that for Git-based package repositories, you need to specify a configuration name explicitly, which is the key used in your repos.yaml configuration file. The example also shows providing a custom destination path ~/my_pkgs. You can omit this if you want Spack to use the default clone location (e.g., ~/.spack/package_repos/<hashed_name>).

After adding, packages from this repository should appear in spack list and be installable.

spack repo remove

To unregister a repository, use its configuration name (the key in repos.yaml) or its local path.

By configuration name (e.g., projectx from the add example):

$ spack repo remove projectx
==> Removed repository 'projectx'.

By path (for a local repo):

$ spack repo remove ~/my_spack_projects/spack_repo/myorg/projectx
==> Removed repository '/home/user/my_spack_projects/spack_repo/myorg/projectx'.

This command removes the corresponding entry from your repos.yaml configuration. It does not delete the local repository files or any cloned Git repositories.

spack repo set

For Git-based repositories, this command allows you to specify a custom local directory where Spack should clone the repository, or use an existing clone. The <config_name> is the key used in your repos.yaml file for that Git repository (e.g., builtin, my_remote_repo).

$ spack repo set --destination /my/custom/path/for/spack-packages builtin
==> Updated repo 'builtin'

This updates your user-level repos.yaml, adding or modifying the destination: key for the specified repository configuration name.

~/.spack/repos.yaml after spack repo set
repos:
  builtin:
    destination: /my/custom/path/for/spack-packages
    # The 'git:' URL is typically inherited from Spack's default configuration for 'builtin'

Spack will then use /my/custom/path/for/spack-packages for the builtin repository. If the directory doesn’t exist, Spack will clone into it. If it exists and is a valid Git repository, Spack will use it.

Repository Namespaces and Python

Package repositories in Spack (from api: v2.0 or newer) are structured to integrate smoothly with Python’s import system. They are effectively Python namespace packages under the top-level spack_repo namespace.

The api: v2.0 repository structure ensures that packages can be imported using a standard Python module path: spack_repo.<namespace>.packages.<package_name>.package. For instance, the mpich package from the builtin repository corresponds to the Python module spack_repo.builtin.packages.mpich.package.

This allows you to easily extend or subclass package classes from other repositories in your own package.py files:

# In a package file (e.g. my_custom_mpich/package.py) in your custom repo
# Import the original Mpich class from the 'builtin' repository
from spack_repo.builtin.packages.mpich.package import Mpich as BuiltinMpich


class MyCustomMpich(BuiltinMpich):
    # Override versions, variants, or methods from BuiltinMpich
    version("3.5-custom", sha256="...")

    # Add a new variant
    variant("custom_feature", default=False, description="Enable my custom feature")

    def install(self, spec, prefix):
        if "+custom_feature" in spec:
            # Do custom things
            pass
        super().install(spec, prefix)  # Call parent install method

Spack manages Python’s sys.path at runtime to make these imports discoverable across all registered repositories. This capability is powerful for creating derivative packages or slightly modifying existing ones without copying entire package files.