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:
- All required dependencies must already be packaged in Debian as
golang-*-devpackages. - These dependencies are installed into the build environment.
- The build system constructs a unified
GOPATHor uses Go's module proxy cache pointing to local directories on the filesystem. - 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.tomlmust 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:
- Built-Using Metadata: Debian packages compiled from
Go or Rust record a
Built-Usingfield in their package metadata. This field explicitly lists the exact versions of all library packages that were statically compiled into the final binary. - Coordinated Rebuilds: When a vulnerability is fixed
in a library like
golang-golang-x-crypto-devorlibrust-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.