Open-source project
kornelski/cargo-deb avatar
kornelski/cargo-deb

cargo-deb: turning a Cargo project into a .deb without a packaging team

Make Debian packages directly from Rust/Cargo projects

578 stars68 forksRustMIT

At a glance

What is it?
cargo-deb is a Cargo subcommand that reads your manifest and produces a Debian package with the metadata, assets, dependencies and maintainer scripts filled in, which is what removes the reason most Rust projects never ship a .deb at all.
Who is it for?
cargo-deb fits a Rust project that needs to ship a .deb and does not want to maintain a separate debian/ directory, because the packaging metadata lives in the same Cargo.toml as the code, so a release tag and a package build come from the same place.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 52 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What it is, and the install

cargo-deb is a Cargo helper command which automatically creates binary Debian packages, in the .deb format, from Cargo projects. The project page is on lib.rs, the documentation is on docs.rs, and the source is MIT licensed. Installing it is two commands.

sh
rustup update   # Bookworm's Rust is too outdated, use Trixie or rustup.rs
cargo install cargo-deb

The comment on the first line is a real constraint rather than a style note. It requires Rust 1.76 or newer, though the crate's own manifest states a rust-version of 1.85, and it optionally needs dpkg, dpkg-dev and liblzma-dev, and it is compatible with Ubuntu. The documentation also gives you an escape hatch, since if the LZMA dependency causes you headaches you can install with cargo install cargo-deb --no-default-features, and if you get a compilation error the advice is to run rustup update, and if that fails, to uninstall the packaged Rust and install the official one instead.

One command produces the package

Running the subcommand from the base directory of your project creates the package, and the output path is predictable.

sh
cargo deb

The file lands in target/debian/ with a name of the project name, the version, a revision of 1 and the architecture, and the location can be changed with the output option. The package can then be installed with dpkg against the generated file, and there is a second form, cargo deb --install, which builds the package and installs it on the local system immediately. That combination is what makes the tool useful in a loop: change a version number, run one command, and the artefact is both on disk and installed.

Metadata comes from the manifest, then from a table you add

No configuration is necessary to make a basic package from a project with a binary. The command obtains the information it needs from the Cargo.toml file, using the Cargo fields name, version, license, license-file, description, readme, and homepage or repository. For a more complete package you may define a new table, package.metadata.deb, containing additional metadata. The example in this project's own manifest shows the pattern, with maintainer, copyright, license-file given as a two-element array of the file and the number of lines to skip at the top, an extended description, depends set to the $auto keyword, a section, a priority, and an assets list. The first author is used as the maintainer if you do not set one, and the list of authors is used for the copyright if you do not set that either, and the package name falls back to the crate name.

The metadata options, and the ones that are easy to miss

Everything in the deb table is optional, and the list is longer than you would guess. The dependency fields are the interesting ones. depends is generated automatically when absent, or when the list includes the $auto keyword, and it picks up the runtime dependencies of the project. pre-depends, recommends, suggests and enhances are all empty by default, and conflicts, breaks, replaces and provides give you the package transition controls. The revision field is the one that surprises people, since it is an additional version of the Debian package used when the package is updated more often than the project itself, and it defaults to 1 but can be set to an empty string to omit it. There is also a features list for choosing Cargo features to build with, and a default-features flag for whether to use the default crate features in addition.

Assets, and the target/release warning

If assets are not specified they default to a special value that takes the binaries built by Cargo, copied to /usr/bin/, and the package's readme, copied into the documentation directory. Each asset entry has three parts. The source is the location of the asset in the Rust project, and glob patterns are allowed. The dest is where the file is copied, and if it starts with usr/lib it is changed to the multiarch path when the multiarch option is enabled, and if it ends with a slash the target is inferred as a directory, otherwise the source is renamed on copy. The mode is the octal permissions string. The documentation then contains the warning that matters most in this project. Always use the target/release/ path prefix for packaging binaries built by Cargo, even if that is not the real path to your target directory, because cargo-deb uses the prefix to detect what to compile and replaces it with the actual target directory path, taking cross-compilation, build profiles, workspaces and custom configuration into account. If you try to fix the hardcoded target/release paths, you will break cargo-deb and make it package stale files and mishandle debug info. Asset entries can also be given as a table, in which case you can override the preserve-symlinks behaviour, and symlinks are defined by a table with dest and link_name entries.

Debug symbols, systemd units and maintainer scripts

Three things beyond the basics are handled by the tool. Debug symbols are stripped from built binaries by default, unless the release profile enables debug in Cargo.toml, with line-tables-only recommended, and the separate-debug-symbols and dbgsym options package the symbols as separate files installed under /usr/lib/debug/ with the build id or path as the name, and this can also be enabled through the deb table. For a package that includes systemd unit files, you may define an inline table, package.metadata.deb.systemd-units, so that the unit files are automatically added as assets and the units are properly installed, with the systemd integration documented separately. And maintainer-scripts points at a directory containing templates, preinst, postinst, prerm or postrm scripts, with a triggers-file option for the dpkg trigger facility and a changelog option for a Debian-formatted changelog file. The project also offers a merge-assets option for combining assets from several sources, documented under Advanced Usage.

Where cargo-deb is the wrong tool

Two boundaries make it the wrong choice. The first is packaging complexity. cargo-deb produces one package from one Cargo project, and the assets list is a flat description of what goes where, so a project with several binaries that need to be split into separate packages, or a project that needs a shared library package installed alongside, is doing the kind of work that debhelper and a hand-written debian/ directory exist for. The second is that cargo-deb stops at the package. It does not build a repository, sign the package, publish it to a mirror, or track the transitions of a package being renamed or split, which is the part of shipping a .deb that most projects eventually need. The project itself is a good guide to what it covers, since the example directory and the test-resources directory are laid out around the packaging metadata rather than around the code.

Editorial conclusion

cargo-deb fits a Rust project that needs to ship a .deb and does not want to maintain a separate debian/ directory, because the packaging metadata lives in the same Cargo.toml as the code, so a release tag and a package build come from the same place. It does not fit a project that has outgrown a single package, because the project assumes one binary per package and the assets list is a flat description of what goes where, and it does not fit a team that needs the full range of Debian tooling, since cargo-deb produces the package rather than managing it. Before you release, read the assets documentation rather than fixing the target/release paths yourself, because the project warns that hardcoding them will make it package stale files and mishandle debug info, and decide whether to enable separate debug symbols before you cut the first release.

Frequently asked questions

How do I install cargo-deb and build a package?

Run rustup update and then cargo install cargo-deb, which requires Rust 1.76 or newer and optionally dpkg, dpkg-dev and liblzma-dev. From the base directory of your project, cargo deb creates the package in target/debian/ with the project name, version, a revision of 1 and the architecture.

Where does cargo-deb get the package metadata from?

For a basic package it reads the Cargo.toml file, using the name, version, license, license-file, description, readme and homepage or repository fields. For anything more complete you add a package.metadata.deb table with options such as maintainer, copyright, depends, section, priority, revision, assets and maintainer-scripts.

Why should I not change the target/release paths in the assets list?

The documentation warns against it. The target/release/ prefix is used to detect what to compile and is then replaced with the real target directory path, accounting for cross-compilation, build profiles, workspaces and custom configuration. Fixing the paths makes cargo-deb package stale files and mishandle debug info.

How do I ship debug symbols and systemd units with a cargo-deb package?

Debug symbols are stripped by default, and the separate-debug-symbols and dbgsym options package them as separate files under /usr/lib/debug/, or you can set separate-debug-symbols in the deb table. For systemd, define an inline package.metadata.deb.systemd-units table so the unit files are added as assets and the units are installed properly.

Official sources

  1. kornelski/cargo-deb on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/kornelski-cargo-deb.svg)](https://hysenlabs.com/projects/kornelski-cargo-deb)

Was this useful?