The package exists, but not for you
A tool is in the AUR, or only as a .deb, or only as a GitHub release. Your distro packaged it two years ago, or never. You end up compiling from source or going without.
Open source — MIT licensed
Zero Layer is a universal Linux package manager. It installs software from 13 different ecosystems onto any distribution by translating binaries at install time — not at run time.
curl -Lo zl https://github.com/polpo21/zero_layer/releases/latest/download/zl-x86_64-unknown-linux-gnu
One binary. No runtime, no daemon, no dependencies.
The problem
Every distribution ships its own format, its own repository and its own idea of where files belong. The software you want is often packaged — just not for your system.
A tool is in the AUR, or only as a .deb, or only as a GitHub release. Your distro packaged it two years ago, or never. You end up compiling from source or going without.
Containers, virtual machines and compatibility layers all solve this by adding a layer. You pay for it forever: startup latency, memory, disk, and a runtime that must keep running.
Dropping a tarball into /opt works until a library moves. Nothing tracks the files, nothing knows the dependencies, and nothing can cleanly undo it.
The idea
A Linux binary is not tied to a distribution — it is tied to a handful of concrete facts: which dynamic linker loads it, and where its shared libraries live. Both are recorded inside the ELF file, and both can be rewritten.
Zero Layer reads those fields with goblin, then rewrites them with elb so they point at your system's real linker and library paths. It does this once, while installing.
After that the binary is simply a native binary on your machine. There is no shim, no wrapper script, no sandbox and no supervising process. That is where the name comes from: the layer is zero.
built for Arch
patched for this machine
Same machine code. It just knows where it lives now.
Philosophy
Zero Layer is opinionated. These are the rules the codebase is held to.
All translation work happens once, during installation. Nothing sits between your program and the kernel afterwards.
Architecture, dynamic linker, libc, library directories and filesystem layout are all probed from the running system instead of hardcoded.
Every install runs inside a transaction that records what it touched. A failure anywhere unwinds the whole operation.
redb instead of SQLite, elb instead of patchelf, rustls instead of OpenSSL. Written in Rust; the few native pieces (compression, crypto) are compiled in, so there is nothing to install next to it.
When a package exists in several ecosystems, Zero Layer shows all of them and lets you choose. It never silently decides for you.
History, rollback, orphan detection and dry-run are core features, not afterthoughts. You can always see what happened and undo it.
How it works
Eight stages run between your command and a working binary. Each one is observable with -v, and any failure rolls the whole thing back.
Finds the package and walks its dependency tree recursively, detecting cycles. If a dependency is missing from the primary source, every other enabled source is queried and you choose.
cross-source resolutionFive classes are checked before anything is written: file ownership, binary names, library sonames, declared conflicts and version constraints.
5 conflict typesPackages are fetched straight from their native repositories over four parallel threads, with progress bars and automatic retry with backoff.
4 threads · retrySHA256 checksums are enforced and GPG signatures are checked when the source provides them. An unverifiable signature is reported differently from a bad one.
SHA256 + GPGThe archive format is detected and unpacked — deb, rpm, tar, zst, xz, nar, squashfs, zip or AppImage — then normalised to a standard FHS layout.
9 archive formatsEvery ELF binary is analysed, then its interpreter and RUNPATH are rewritten to match your system. Multi-binary packages are patched in parallel; static binaries are correctly left alone.
goblin + elbFiles are placed atomically inside a transaction that records every file, directory, symlink and database row it creates. Any error unwinds all of it.
atomic · rollbackFiles, dependencies, ownership and the operation itself are written to an embedded database, which is what makes why, size, doctor and rollback possible later.
redb + graphCapabilities
Everything you would expect from your distro's tooling, plus the things it never had.
ELF binaries are analysed with goblin and patched with elb at install time. The dynamic linker and RUNPATH are rewritten to match your system, so packages run at full native speed afterwards.
Every enabled source is queried simultaneously, results are scored by relevance, and AUR binary variants are discovered automatically.
An install either completes or leaves no trace. No half-written state, ever.
doctor, audit, why, size, diff and history answer the questions package managers usually leave you guessing about.
Install versions side by side and switch between them, or spin up an ephemeral environment that disappears when you exit the shell.
Checksums and signatures on the way in, CVE lookups against OSV.dev on the way out, plus pinning to freeze anything you do not want moving.
Sources
Each source is a plugin implementing the same trait. Enable the ones you want per machine, or filter per command with --from.
pacmanArch LinuxaurArch User RepositoryaptDebian · UbuntudnfFedora · RHELzypperopenSUSE · SLESapkAlpine LinuxxbpsVoid LinuxportageGentoo binhostnixnixpkgsflatpakFlathubsnapSnapcraft StoreappimageAppImageHubgithubGitHub ReleasesSource availability depends on the upstream being reachable. The repository tracks the live status of every plugin.
Commands
One binary, twenty-five commands, shell completions for bash, zsh and fish.
$ zl install firefox # pick from every source $ zl install firefox --from pacman # force one source $ zl install sharkdp/bat --from github $ zl install python --version 3.12 # side by side $ zl remove firefox --cascade # drop orphaned deps $ zl --dry-run install firefox # preview only $ zl run neofetch # run without installing
$ zl search vim # all sources, in parallel $ zl search vim --exact # exact names only $ zl search vim --from pacman,apt # limit the sweep $ zl info ripgrep # full metadata $ zl list --explicit # what you asked for $ zl sources list # enabled / disabled $ zl sources only pacman aur github
$ zl doctor # full system health check $ zl why libpcre2 # which package pulled it in $ zl size --sort # disk usage, largest first $ zl diff firefox # what an update would change $ zl audit # known CVEs via OSV.dev $ zl cache dedup # hardlink duplicate libs
$ zl history list # every operation, timestamped $ zl history rollback # undo the last one $ zl history rollback 3 # undo the last three $ zl pin firefox # freeze this version $ zl export # write zl-lock.json $ zl import zl-lock.json # reproduce elsewhere
$ zl env shell # throwaway env, dies on exit $ zl env shell myproject # named, persists $ zl env list $ zl env delete myproject $ zl switch python 3.12 # activate another version $ zl completions zsh # bash · zsh · fish
Under the hood
Predictable layout on disk, a Rust codebase with native code kept to a minimum, and a test suite that runs on every push.
Everything lives under one directory you own. Add bin to your PATH and nothing else on the system is touched.
~/.local/share/zl/ ├── bin/ symlinks — add to PATH ├── lib/ shared libraries ├── share/ data files ├── etc/ configuration ├── packages/ one dir per name-version ├── cache/ downloads ├── envs/ ephemeral environments └── zl.redb package database
Comparison
Containers and universal package formats solve the same problem by isolating software. Zero Layer solves it by adapting software.
| Tool | Approach | Runtime cost | Disk cost | Needs a daemon |
|---|---|---|---|---|
| Zero Layer | Patch binaries at install | None | Shared libraries, deduplicated | No |
| Docker / Podman | Full OS image per app | Process + namespace overhead | Image layers per app | Yes |
| Flatpak | Bundled runtimes | Sandbox + portal indirection | Runtime per app family | Yes |
| Snap | Squashfs + confinement | Mount + confinement layer | Compressed image per app | Yes |
| Manual tarball | Unpack and hope | None | Whatever you copied | No |
Manual installs match Zero Layer on overhead — and lose on everything else: nothing is tracked, nothing resolves dependencies, and nothing can undo it.
Get started
One download, one line in your shell profile. No package manager needed to install the package manager.
v0.3.1 is published, with prebuilt binaries for x86_64 and aarch64, glibc and musl. Everything described on this page ships in it — no compiler needed. Release notes →
Grab the latest release for your architecture. Use the second form if you would rather not use sudo.
$ curl -Lo zl https://github.com/polpo21/zero_layer/releases/latest/download/zl-x86_64-unknown-linux-gnu $ chmod +x zl && sudo mv zl /usr/local/bin/ # or, without sudo: $ mkdir -p ~/.local/bin && mv zl ~/.local/bin/ # other builds: zl-x86_64-unknown-linux-musl, # zl-aarch64-unknown-linux-gnu, zl-aarch64-unknown-linux-musl
Zero Layer symlinks the executables it installs into its own bin directory.
# bash (~/.bashrc) or zsh (~/.zshrc) export PATH="$HOME/.local/share/zl/bin:$PATH" # fish fish_add_path ~/.local/share/zl/bin
Leave off --from and Zero Layer will search every enabled source and let you pick.
$ zl install ripgrep $ zl install sharkdp/bat --from github $ zl doctor # confirm everything is healthy
Prefer to build it yourself? Requires Rust 1.90 or newer. cargo build --release →
FAQ
~/.local/share/zl. Root is only relevant if you choose to put the zl binary itself in /usr/local/bin, and even that is optional.--dry-run.Free and open source under the MIT licence. No account, no telemetry, no daemon.