I hate packaging my software for Linux
41 points by mtlynch
41 points by mtlynch
The question that's not really answered is why are you the one packaging it? Most Linux distributions and the BSDs maintain package repositories. If you have well-written build environments and it's easy to build in a pristine not-networked environment, then package maintainers can easily add your program to the things that they ship. If you're packaging it yourself, this normally means one of three things:
If it's not one of those, I'm curious what it is.
It's not quite so easy to get packages in distributions, in my experience. The reasons are actually what you list, although they are much more common than you might think.
It's painful to build.
Many newer languages (or even languages that are not so new at all anymore) are painful from the point of view of classic Linux distributions, which are heavily focused on what C/C++ needs and provides. In particular, modern dependency-heavy languages (even the ones that are not so bad as Javascript) are "painful to build" for e.g. Debian unless major infrastructure is in place, even if they are not painful at all to build using their own native build tools. The usual problem is that all of the dependencies also need to be their own individual packages, and unless there is tooling to make that pain-free, it is just not going to happen given the amount of version churn in dependency-heavy languages. (Which today is most of them!)
There's no pull from users on that platform wanting it.
This is a chicken-and-egg problem. How would there be demand for newly constructed software if users cannot run it? This actually exacerbates the pain problem - nobody minds packaging a dependency-free C or Python package, but I had a lot of trouble getting Haskell software into Debian, because the dependencies had to be their own packages. Further, once it's there, it doesn't seem to get updated automatically either.
The article seems to have an answer to this: "As explained in this other hn discussion I hadn't gotten around to pushing my .deb as a Debian (and Ubuntu) official package, because it requires all the (many) rust dependencies to also be Debian packages. [...] I can't just vendor all my deps as sources into my deb source package, that goes against the policy."
That's a bit of a silly restriction from Debian, since these things are statically linked, so updating the dependency won't actually fix it. It really should just be some SBOM-like metadata.
They want to be able to rebuild the binary package from its source package using only what is in Debian, not what is on Cargo at any random point in time.
I feel like this ought to be easy in principle, which means I'm definitely missing what some of the difficulties are and I'd like to know. This comment is a question, not an assertion (and especially not an accusation).
In theory couldn't this be pretty much entirely automated by doing the following?
Steps 1 and 4 have to be implemented for each package build/management tool (cargo, npm, cabal, etc) but I think any tool which doesn't have the ability to do both of those things is just a nightmare for CI anyway and needs to be fixed.
Yes, it could be done as you suggest (I guess, I'm not a Debian developer). This does lead to an enormous amount of dependency packages, which may be culturally or technically impractical. I think there is also the problem that a lot of these package managers do not allow multiple versions of the same package to be installed, so what happens if two packages depend on mutually incompatible versions of a dependency?
Nixpkgs basically does what you suggest, but it still requires a nontrivial amount of maintenance (although I'm only familiar with its Haskell infrastructure), and Nix is of course uniquely well suited to this kind of thing.
That's pretty much what the FreeBSD ports system does.
Cargo and Go both support building in offline environments, they can give you the list of things to download and also take a location that caches them. FreeBSD packages support this by running the relevant tool to walk the graph of things it will download and embedding those in the port description as tarballs that need to be downloaded and their hashes. These are then cached in the distfiles cache for the FreeBSD package build infrastructure. When you build a port for a Go or Rust thing in Poudriere (which runs the builds with no Internet access in the package-build system), these are all downloaded into the pristine jail that will run the build, then extracted and the build runs with no network access. The collection of files that need to be downloaded is listed in the manifest.
I'm not sure how big of an issue this is, but cargo does its own version resolution and allows multiple versions of the same dependency in a single tree and packages rely on this for builds. If debian only wants to package one version of each dependency and supersede Cargo's version solving then they need to patch dependencies (which aiui, is exactly how they deal with C libraries).
I feel like the very imperfect solution to this is to generate a unique debian package for every (crate, version) pair.
A "boil the oceans" approach might be to say, hey, let's not support that and only package applications that work when every library is overridden to a single version of that library. The ocean boiling part would be the part where it indirectly creates pressure to avoid using old versions of libraries which break their APIs.
Having one package per dependency is not the ultimate goal. The main goal behind this requirement is to handle security. This is completely defeated if you automate the building of NxM packages.
I think it's just an overall desire to get adoption, they're spraying-and-praying at package managers to hope to reduce friction
One extra reason that is missing from your list:
I am probably in a minority even among the possible target audience but I look for easy to build rather than easy to install. I don't like to use software that I can't easily build myself and there is no way I'd run a blind curl ...|sh. I had a strong aversion to that even in more innocent times and in 2026 it's out of the question.
Yup, a Rust thing that I can do cargo build in an ephemeral container and copy the binary out is great. And, if it's easy to build like this, it's easy for me (or, ideally, someone else) to create a FreeBSD port for and then I can pkg install it. But if it's annoying to build, it's going to be hard to package.
I think you might've overengineered yourself on accident. Most software for Linux usually doesn't bother trying to accommodate to this many distro's platforms and trying to play nice with all of them, because as you're noticing, it's quite painful to do so.
Usually it's fine enough to just distribute the source tarball, a "universal" execution mechanism (static linking/AppImage) and a distro package for whatever you're the most familiar with/are willing to support the most.
A lot of the *-bin packages on the AUR just download .deb releases and unwrap the binary; it's okay to not be perfectly compliant.
As for the "just provide a URL", you can just host a custom repository? For debian at least, it's easy enough to the point you just need a static filehost and write some metadata files+your package files. I know you can also do it with Ubuntu (ignore PPAs, they're a badly designed Canonical abstraction) and Fedora, but I'm not too familiar with how to do it on Fedora.
For Debian/Ubuntu, you have other ways around official inclusion into their repositories. For Ubuntu, you can build and host on Launchpad. For Debian, we don't have anything useful, so you could self-host (with reprepro or aptly to maintain a repository) or use things like openSuSE build service (which as the name implies is also a build service, but it is a bit clunky for Debian packages, but on the upside, it supports both RPM and Deb packages) or Package Cloud.
In this case, absolutely do not follow the official guidelines. You'll run into a wall quickly. Instead, workaround all those rules, but you can keep using the official tools. See https://vincent.bernat.ch/en/blog/2019-pragmatic-debian-packaging (a bit dated, but I think it is still relevant).
But like a sibling comment said, you shouldn't bother too much. A lot of software is not packaged in Debian/Ubuntu due to impracticality. Users have to find workarounds. Myself, I fall back to nixpkgs. A self-updating binary would be another solution.
So I'd have to build in an old Ubuntu container image and inherit its unpatched packages into my build - or drop those users entirely. Neither option holds up.
I've had good, but limited, experince using zig cc as a linker for this, good enough that I integrated the option into my openD language distribution as --target=linux-compat which expands to first making a shell script that does zig cc --mtriple=x86_64-linux-gnu.2.17, the special bit being gnu-2.17 which specifies I want compatibility with the older glibc. Then it invokes the D compiler with -gcc=path_to_that_script so it thinks zig is just a special linker.
The zig build thing then generates a sub library on-demand to link against and the resulting binary works on both old and new systems. And as a fun bit, if I replace x86_64 with aarch64 I can cross compile and copy the system to a raspberry pi without installing a whole sysroot either.
I wrote some details in my blog a few months ago: https://dpldocs.info/this-week-in-arsd/Blog.Posted_2026_05_05.html#linux-compat
Then I dynamic load with dlopen other libs as-needed so it can adapt to version differences at runtime (like if I only find openssl 1.0, it can polyfill the missing functions, doesn't do everything but works well enough for your typical https download with minimal effort.)
I should note the other alternative here is to install the sysroot - keep the old target system's root dir someone on your filesystem and pass -sysroot to gcc etc to tell it to use that. But I like the zig thing more.
Of course, there's also the static link musl libc the author landed on but I don't like that because of its own random incompatibilities, like I've had trouble doing dns lookups before with that, idk if it is me doing it wrong or if it was fixed in the future, but still, I prefer to stick to glibc. And note even if you opt into the older api compatibility, it still works with newer versions of the library; glibc does ship its own backward binary compatibility layer built in, that's why you see those symbol not found errors, it versions them and knows. Just telling it at link time you want compatibility has been a hassle historically. That's why I'm impressed with zig for successfully doing something about it.
otherwise i agree with the author that linux is still a pita, lol, it is generally easier to run random Windows .exes on linux than it is to run linux programs on linux.
Personally I just release Linux software as a statically linked binary in a tarball. Depending on the implementation language and what the program does, static linking can be a hassle, but making binary packages for N distributions is a few yaks too much for me.
I only package my software for the distro i use. Long it was arch, so i made AUR packages for it. Now i use NixOS and i simply put a flake with package derivations in my repo. For other distros i would probably target flatpak.
I'm pretty comfortable adding a new repository for apt. I do this for Sublime Text and Incus.
The problem with these older packagers - and even some of the newer ones - is that it's annoying to run a repository, and it's really annoying to integrate with their PGP stuff.
There's a giant mountain of cruft to climb here. But in my opinion a modernized signature/trust architecture is job number 1.
Flatpak [...] It's designed for self-contained sandboxed desktop GUI applications and I'm releasing a terminal-based TUI that allows you to rampage around your machine and network
I feel like this deserves more of an explanation and less seemingly being proud of over-requesting permissions.
This is 80% of why I made yeet for packaging Anubis. The main Anubis yeetfile.js is a bit complicated, but it has to be because this is an actually hard problem.
Halfway through I was about to say "just release a statically linked binary"! That's what /usr/local/bin (and ~/.local/bin) are there for!
The reason different people do the packaging is to avoid the "garbage-problem". Packages in official repositories usually have some kind of "this is maintained" (or at least some kind of "this works") guarantee, a security tracking, etc.
I think the "art of distribution packaging" is a somewhat underrated art form. It's not possible to compare deb/rpm/... to crates/npm/.... These are different kinds of things, with heavily different goals, but not many people understand the actual difference.
For fedora (and its downstream) users, if you can already build a binary RPM from a source RPM in a clean environment, one nice addition would be to publish your source RPM into a COPR.
COPR builds your source RPMs into a repository for the platforms you specify, and publishes the repository in a way that is easy for Fedora users to add to their systems' repository lists. Then whatever means they use to update their systems normally will catch updates to your program as well, without needing to get the program into the official repositories.
I've never tried to get a package into fedora officially, because COPR was good enough for my (small) audience, and I have no idea whether fedora is as challenging as debian wrt packaging rust. I kind of suspect they'd allow (and maybe require) you to vendor your dependencies if they weren't already packaged. But that's a hunch based on vibes, as opposed to direct experience.
Is there anything else I can do?
Make sure it's easy to build/package by other people. You shouldn't have to provide the packages for every single distro. The distro maintainers will do it for you, if the build process is boringly straightforward.
State all your dependencies with versions. Document the build process (eg. cmake/make install, meson/ninja, etc...) Follow the "trends" for these build system, don't be too smart.
Installing a software should only be a matter of 3 commands: install deps, build, install.
I feel your pain. During PhD, i tried to package a simulator and it wasnt easy.
Open build service from opensuse is a step in right direction. It needs a bit more love.
Yeah, packaging for Linux distros is usually a huge mess of having to juggle 11 different things for each of them and that's not even counting packaging software for their official repos which usually comes with another 50 layers of restrictions.
For Linux, and even Mac, you’d think the nix install method would be the catch-all. Very few have disk space constraints that they can’t afford nixpkgs.
But I forget sometimes how constrained some user environments are at the labs. They can’t use sudo and literally need to file a ticket for everything out of the ordinary.
There is cosmopolitan: blog post github
Here is their blurb: Cosmopolitan Libc makes C/C++ a build-once run-anywhere language, like Java, except it doesn't need an interpreter or virtual machine. Instead, it reconfigures stock GCC and Clang to output a POSIX-approved polyglot format that runs natively on Linux + Mac + Windows + FreeBSD + OpenBSD 7.3 + NetBSD + BIOS with the best possible performance and the tiniest footprint imaginable.
This is written in Rust, though.
It could be adapted either direction I'm sure.
I think it's an interesting possible answer that many may not be aware of. It's probably not the right answer, but it does so cleverly by sidestepping most of the packaging problems to simply copying a single binary and running it. Which is why I thought it worth sharing. I've played with it some and it's fairly solid. The utilities they have already ported to the tool have made my life quite easy sometimes.
Personally, I think I'd be uncomfortable with its level of cleverness. However, I have no first-hand experience with it.
I think the big problem with creative solutions like this, when you eventually run into problems, it can get really complicated to sort out. You often find yourself out in mostly unsupported/unreplicatable land and that can be an uncomfortable place to be. The good news is, if you persevere, you will be sure to learn a lot.