Packaging Rust Crates and Go Modules in Debian

Debian handles modern programming ecosystems like Rust and Go by reconciling their decentralized, vendored dependency models with Debian’s strict policies on software freedom, system-wide dependency management, and reproducible builds. Rather than allowing language-native package managers like Cargo or go get to pull uncontrolled code from the internet during compilation, Debian isolates dependencies into native .deb packages. Through specialized packaging teams and custom automation tooling, Debian unvendors libraries, enforces system-level dependency resolution, and tracks shared source components to maintain long-term security and auditability.

The Architectural Conflict

Language ecosystems like Rust and Go were designed around fast-moving, centralized registries (crates.io and Go module proxies) that favor static linking, lockfiles, and deep dependency graphs. Developers in these ecosystems routinely vendor code or pin exact micro-versions of dozens of transitive dependencies.

In contrast, the Debian Policy Manual enforces requirements that directly oppose this model:

  • No network access during builds: Build daemons (buildds) run in offline environments to ensure reproducibility and security.
  • No code duplication: Libraries should be packaged once and shared, avoiding embedded code copies that obscure security vulnerabilities.
  • Strict licensing: Every single source file must comply with the Debian Free Software Guidelines (DFSG) and be documented in machine-readable copyright files.
  • Security tracking: If a library has a Common Vulnerabilities and Exposures (CVE) entry, the security team must be able to patch the library rather than track down hundreds of statically bundled binaries.

To bridge this gap, Debian treats both Rust crates and Go modules as distinct Debian source and binary packages.

Packaging the Go Ecosystem

Debian’s Go packaging is overseen by the Debian Go Packaging Team (pkg-go). Go projects usually produce statically linked binaries, but Debian requires the underlying libraries to exist as independent source packages within the archive.

Tooling and Automation

The primary tool used is dh-make-golang. This utility inspects a Go repository's go.mod file, downloads the required source code, determines licensing, and generates the scaffolding for a standard Debian package. It maps Go import paths directly to Debian package names using a predictable convention, typically golang-github-<user>-<repo>-dev.

The Unvendoring Requirement

Debian policy generally forbids the use of bundled vendor/ directories when building packages for the main archive. Package maintainers strip vendored directories from the upstream source tarballs using Files-Excluded in debian/copyright or via repackaging scripts.

When a Go application (such as Docker, Hugo, or Kubernetes components) is built:

  1. All required dependencies must already be packaged in Debian as golang-*-dev packages.
  2. These dependencies are installed into the build environment.
  3. The build system constructs a unified GOPATH or uses Go's module proxy cache pointing to local directories on the filesystem.
  4. The Go compiler builds the binary using only the Debian-provided source libraries.

Packaging the Rust Ecosystem

Rust presents a distinct challenge due to Cargo's strict Semantic Versioning (SemVer) requirements, deep dependency trees, and micro-crate culture. The Debian Rust Team (pkg-rust) maintains the infrastructure required to represent crates as Debian packages.

debcargo and the Local Registry

Debian developers do not hand-craft packaging rules for thousands of crates. Instead, they use debcargo, a tool that reads a crate’s Cargo.toml, checks its license metadata, applies Debian-specific patches, and outputs a Debian source package.

These packages are built into binary packages named librust-<crate-name>-dev. When installed, these packages drop their source code into a standardized system-wide registry located at /usr/share/cargo/registry.

Debian's version of Cargo is patched to replace network lookups with this offline, file-system-based registry. When a Rust binary package is built:

  • Cargo is redirected to /usr/share/cargo/registry.
  • All dependencies declared in Cargo.toml must be present in that directory as Debian packages.
  • Cargo compiles the binary offline using the system-installed crate sources.

Dependency Relaxation

A major point of friction is Cargo’s tendency to pin dependencies to minor or patch versions. If crate A requires serde = "1.0.100" and crate B requires serde = "1.0.104", Cargo typically resolves this smoothly, but if crate C requires serde = "0.9", two incompatible versions exist.

Debian cannot maintain hundreds of distinct versions of the same crate due to the maintenance overhead. Consequently, maintainers use debcargo.toml configuration files to apply patches to Cargo.toml upstream files. These patches relax version constraints, forcing crates to build against a single, standardized version of a dependency available in Debian, provided the API remains compatible.

Managing the Security Overhead of Static Linking

Because both Go and Rust output statically linked binaries, a vulnerability in an underlying library affects every compiled application that includes it.

To manage this:

  1. Built-Using Metadata: Debian packages compiled from Go or Rust record a Built-Using field in their package metadata. This field explicitly lists the exact versions of all library packages that were statically compiled into the final binary.
  2. Coordinated Rebuilds: When a vulnerability is fixed in a library like golang-golang-x-crypto-dev or librust-openssl-dev, security trackers automatically identify all reverse dependencies. The Debian build infrastructure then schedules non-maintainer uploads (NMUs) or binary rebuilds (binNMUs) across the archive to recompile all affected binaries with the patched library.

Through heavy automation, continuous patching of version constraints, and custom integration with local build registries, Debian successfully incorporates modern Rust and Go ecosystems while preserving its core principles of software transparency, auditability, and offline reproducibility.