software

Hermetic Builds

About hermetic builds and dependency vendoring

What are Hermetic Builds?

A hermetic build is a build process that is self-contained, deterministic, and completely air-gapped from the internet. It produces the same output given the same inputs, regardless of the environment in which it runs. The term “hermetic” comes from the concept of a hermetically sealed container – nothing gets in or out.

In the context of software development, hermetic builds ensure that your application can be built consistently across different environments without relying on external resources during the build process. This is primarily achieved through dependency vendoring – the practice of including all necessary dependencies directly within your project repository.

Think of hermetic builds as the “airplane test” for your entire build system. If you can build your project on an airplane with no internet connection, no access to external systems, and the only machine involved in the build is the one on your lap, you’ve achieved a truly hermetic build.

Why Hermetic Builds Matter Beyond Security

While supply chain security is often the headline reason for adopting hermetic builds, the benefits extend far beyond just protecting against malicious dependencies. Hermetic builds fundamentally change how you think about software reliability, debugging, and team productivity.

1. Debugging and Troubleshooting Become Tractable

When a build fails in a non-hermetic environment, you’re often left wondering: Was it a network issue? Did a dependency registry go down? Did someone push a breaking change to a transitive dependency?

With hermetic builds, you eliminate an entire class of failure modes. When something breaks, you know it’s either:

This dramatically reduces the scope of investigation and makes debugging more predictable. No more debugging sessions that end with “it must have been a network hiccup” or “try running it again.”

2. Freezing Your Dependencies in Time

Hermetic builds create a time capsule for your dependencies. When you vendor dependencies, you’re preserving the exact state of the software ecosystem at the time of your last dependency update. This has profound benefits:

This is particularly valuable for teams maintaining long-lived applications or working in regulated industries where you need to prove exactly what was running at any given time.

3. Performance and Productivity Gains

The performance benefits of hermetic builds compound over time:

Consider a typical CI/CD pipeline that spends 2-3 minutes downloading dependencies on every run. Over hundreds of builds per day across a team, this time adds up to hours of wasted compute and developer waiting time.

4. Eliminating the “Works on My Machine” Syndrome

Non-hermetic builds are susceptible to subtle environmental differences:

Hermetic builds eliminate these variables. If the build works in one environment, it will work in all environments with the same base toolchain.

The Supply Chain Security Bonus

While not the only reason to adopt hermetic builds, supply chain security benefits are substantial:

Protection Against Dependency Substitution Attacks

Vendoring dependencies reduces exposure to dependency confusion, package squatting, and typosquatting attacks. Because the build uses the exact dependency source and versions committed to the repository, it does not dynamically resolve packages from a registry where an attacker-controlled package could be selected instead.

Immutable Dependency Snapshots

Vendored dependencies create an immutable snapshot of your dependency tree. Even if an attacker compromises a package registry and replaces legitimate packages with malicious ones, your builds continue using the known-good versions you’ve vendored.

Audit Trail and Compliance

Every dependency update becomes an explicit, reviewable change in your version control system. This creates a clear audit trail of what changed, when, and who approved it – crucial for compliance in regulated industries.

Implementing Hermetic Builds: Language-Specific Strategies

Ruby: The Gold Standard

I’m biased, but Ruby makes dependency vendoring such a joy.

Ruby’s Bundler makes hermetic builds straightforward:

bundle install --local

This assumes you have a vendor/cache directory containing all your gems and a .bundle/config file like this:

---
BUNDLE_BIN: "bin"
BUNDLE_PATH: "vendor/gems"
BUNDLE_CACHE_PATH: "vendor/cache"
BUNDLE_CACHE_ALL: "true"
BUNDLE_SPECIFIC_PLATFORM: "true"
BUNDLE_NO_INSTALL: "true"

Ruby gems are single compressed files, making them ideal for vendoring without repository bloat.

Here is an example of a Ruby project that uses vendoring effectively: GrantBirki/ruby-template

Python: Hashed, Platform-Specific Wheels

Python projects can build hermetically by exporting a hash-locked requirements file, committing wheels for each supported platform, and forcing the installer to use only that cache:

# Lock and export exact dependencies with hashes
uv lock
uv export --frozen --format requirements.txt \
  --output-file vendor/requirements.lock.txt

# Match these values to your pinned Python version
PYTHON_SERIES=3.13
PYTHON_ABI=cp313

# Download Apple Silicon wheels for macOS development machines
python -m pip download --require-hashes --only-binary=:all: \
  --platform macosx_11_0_arm64 \
  --python-version "$PYTHON_SERIES" \
  --implementation cp \
  --abi "$PYTHON_ABI" \
  --dest vendor/cache/python/macos-arm64 \
  -r vendor/requirements.lock.txt

# Download x86_64 wheels for Linux CI runners
python -m pip download --require-hashes --only-binary=:all: \
  --platform manylinux2014_x86_64 \
  --python-version "$PYTHON_SERIES" \
  --implementation cp \
  --abi "$PYTHON_ABI" \
  --dest vendor/cache/python/linux-x86_64 \
  -r vendor/requirements.lock.txt

# Install offline on Apple Silicon development machines
python -m pip install --require-hashes --no-index \
  --find-links vendor/cache/python/macos-arm64 \
  -r vendor/requirements.lock.txt

# Install offline in Linux CI
python -m pip install --require-hashes --no-index \
  --find-links vendor/cache/python/linux-x86_64 \
  -r vendor/requirements.lock.txt

Wheels can be specific to the Python version, ABI, operating system, and architecture, so vendor a cache for each environment you support. Bootstrap tools such as uv must also be available without network access.

Here is an example of a Python project that vendors both its application dependencies and bootstrap tooling: GrantBirki/dns.

Go: Built-in Vendoring Support

Go has excellent built-in support for hermetic builds:

# Create vendor directory with all dependencies
go mod vendor

# Build using only vendored dependencies
go build -mod=vendor

You might end up with a pull request containing +/- 100,000 lines of code, but this is the price of reliability.

Here is an example of a Go project that uses vendoring effectively: GrantBirki/go-template.

The Airplane Test: Your Hermetic Build Validation

Here’s how to validate that your builds are truly hermetic:

  1. Disconnect from the internet (or work from a flight!)
  2. Clone your repository to a clean directory
  3. Bootstrap your development environment using your standard setup scripts
  4. Run your tests and build your application
  5. Start your application (if applicable)

If any step fails due to network dependencies, your build isn’t hermetic yet.

Common Objections and Responses

“But vendoring increases repository size!”

Yes, vendoring increases repository size, but consider the trade-offs:

“Dependencies become stale!”

This is actually a feature, not a bug. Dependencies should be updated intentionally and deliberately, not automatically. Hermetic builds force you to be conscious about dependency updates, leading to more stable software.

“What about security updates?”

Hermetic builds don’t prevent security updates – they make them explicit and reviewable. When a security issue is discovered, you update the affected dependencies, review the changes, and commit the new vendored versions.

Best Practices for Hermetic Builds

  1. Automate dependency updates: Use tools like Dependabot or Renovate to propose dependency updates as pull requests
  2. Regular dependency audits: Periodically review and update dependencies, don’t let them become too stale
  3. Document your vendoring process: Make it easy for new team members to understand how to update dependencies
  4. Test dependency updates thoroughly: Since updates are explicit, you can afford to test them more rigorously

Security tip: if you are going to use dependabot to update your dependencies, you should use the cooldown feature to set a waiting period before a new dependency can be pull into your project like this. It will help shield you from supply chain attacks.

Hermetic Builds and SLSA Level 3

The Supply Chain Levels for Software Artifacts (SLSA) framework provides a set of security guidelines for securing software supply chains. SLSA Level 3 represents a significant milestone in supply chain security, and hermetic builds are a critical component for achieving this level.

What SLSA Level 3 Requires

SLSA Level 3 builds upon the requirements of lower levels (SLSA L1/L2) and adds several key guarantees:

How Hermetic Builds Enable SLSA Level 3

Hermetic builds directly support two of the three core SLSA Level 3 requirements:

  1. Build Isolation: By vendoring all dependencies and eliminating external network calls during the build process, hermetic builds naturally prevent builds from influencing each other. Each build operates in a completely self-contained environment.

  2. Provenance Integrity: Since hermetic builds use only explicitly vendored dependencies, the provenance can accurately capture the complete set of inputs used in the build. There are no hidden or implicit dependencies that could affect the build outcome.

When combined with a SLSA Level 3 compliant build platform (like GitHub Actions with the appropriate generators), hermetic builds provide the foundation for achieving the highest level of supply chain security currently defined by the SLSA framework.

The Path Forward

If your organization is pursuing SLSA Level 3 compliance, implementing hermetic builds should be one of your first priorities. They provide the build isolation and reproducibility guarantees that SLSA Level 3 requires, while also delivering the practical benefits discussed throughout this post.

The Bottom Line

Hermetic builds represent a fundamental shift in how we think about software reliability. They transform builds from fragile, network-dependent processes into predictable, reliable operations. While the security benefits are significant, the real value lies in the dramatically improved developer experience and system reliability.

In a world where software systems are increasingly complex and interconnected, hermetic builds provide a foundation of stability that teams can build upon with confidence. They’re not just a nice-to-have feature – they’re a prerequisite for professional software development and a critical component for achieving advanced supply chain security standards like SLSA Level 3.

Hermetic builds: Because your software should build the same way every time, everywhere.