Package registries differ in dozens of ways, but most of those differences cluster into a few structural categories. Looking at them through the lens of design tradeoffs helps explain why they ended up where they did. The ecosyste.ms documentation repositories contain detailed data on over 70 registries; here I’m trying to draw out the shapes.
The categories below are roughly orthogonal dimensions. No registry is “just” one thing; each is a particular combination of choices. npmjs.com is database-backed, unreviewed, has flat-plus-scoped names, ships mostly source, and is run by a for-profit company. Debian’s repositories are filesystem-based, curated by maintainers, use distro-managed names, ship binaries, and are run by a foundation. Those combinations matter more than any single axis.
A companion post covers package manager clients: resolution algorithms, lockfiles, build hooks, and manifest formats. The data is also available as CSV. There are gaps; contributions welcome.
Contents: Architecture · Review model · Namespacing · Distribution model · Governance · Ecosystem scope · Version retention · Size · Mirroring
Registry architecture
How does the registry store and serve package metadata?
Database-backed web services are uploaded via API, with metadata in Postgres or similar. This model scales well and supports rich features like download counts and vulnerability reporting.
- npmjs.com
- pypi.org
- rubygems.org
- nuget.org
- crates.io1
- packagist.org
- hex.pm
- pub.dev
- clojars.org
- forge.puppet.com
- anaconda.org
- luarocks.org
- community.chocolatey.org
- open-vsx.org
- galaxy.ansible.com
- jsr.io
Git repositories as indexes use version control as the storage layer on the critical path. If you removed git, you’d need to replace it with a database. The git model provides history, is trivially mirrorable, and works offline once cloned. But it doesn’t scale indefinitely; Cargo had to add a sparse index to avoid downloading the entire registry on first use.
- homebrew-core
- cocoapods.org
- vcpkg
- conan.io
- swiftpackageindex.com
- Julia General registry
- juliahub.com2
- winget-pkgs
- spack
Filesystem-based repositories serve generated index files statically from HTTP mirrors. The server does work only when the repository is updated, not when clients fetch. This is the pattern that the compact index brought to RubyGems.
- apt/dpkg
- yum/dnf
- pacman
- apk
- zypper
- Portage
- cran.r-project.org
- bioconductor.org
- metacpan.org
- hackage.haskell.org
- pkgs.racket-lang.org3
- FreeBSD ports
- pkgsrc
- Helm
- postmarketOS
- Adélie Linux
Source host as registry means no central registry. Packages are fetched directly from git hosts using URLs as identifiers.
- Go modules
- Deno
- Carthage
Content-addressed stores identify packages by hash of inputs. Binary caches provide pre-built artifacts.
- Nix
- Guix
Reviewed / Unreviewed
Does someone look at packages before they’re available?
Unreviewed means anyone can publish immediately. You create an account, run a publish command, and your package is live within seconds. This enables growth but creates attack surface.
- npmjs.com
- pypi.org
- crates.io
- rubygems.org
- packagist.org
- nuget.org
- hex.pm
- pub.dev
- clojars.org
- juliahub.com
- hackage.haskell.org
- metacpan.org
- forge.puppet.com
- anaconda.org
- luarocks.org
- open-vsx.org
- galaxy.ansible.com
- jsr.io
Reviewed registries have maintainers review packages before they appear. These registries grow more slowly but catch problems earlier. In practice, “review” ranges from packaging QA and policy checks to security vetting; very few projects do systematic source code review.
- Debian
- Fedora
- Ubuntu
- homebrew-core
- Alpine
- Arch4
- nixpkgs
- F-Droid
- cran.r-project.org
- bioconductor.org
- conda-forge
- postmarketOS
- Adélie Linux
- spack
- FreeBSD ports
- pkgsrc
- winget-pkgs
- central.sonatype.com5
Moderated upload accepts uploads but has moderation layers or automated semantic checks.
Namespacing
How are packages named?
Flat namespaces give each package a single global name.
- rubygems.org
- pypi.org
- crates.io
- hex.pm
- hackage.haskell.org
- cran.r-project.org
- juliahub.com
- package.elm-lang.org
- luarocks.org
- community.chocolatey.org
Scoped namespaces add organizational prefixes like @babel/core or symfony/console.
- npmjs.com
- packagist.org
- forge.puppet.com
- open-vsx.org
- galaxy.ansible.com
- winget-pkgs
- artifacthub.io
- anaconda.org
- jsr.io
Hierarchical namespaces use structured naming like org.apache.commons:commons-lang3 or DateTime::Format::Strptime.
- central.sonatype.com
- metacpan.org
- clojars.org
URL-based identifiers like github.com/user/repo use domain ownership as the claim. No registration step.
- proxy.golang.org
- deno.land
- Swift Package Manager
- Carthage
Distro-managed names are controlled by distribution maintainers, often differing from upstream project names.
- Debian
- Fedora
- Arch
- Alpine
- homebrew-core
- nixpkgs
- spack
- conda-forge
- FreeBSD ports
- pkgsrc
- postmarketOS
Distribution model
What gets distributed?
Source only ships code that gets compiled or interpreted on the client. One artifact supports any platform.
- npmjs.com
- crates.io
- proxy.golang.org
- metacpan.org
- hex.pm
- hackage.haskell.org
- cran.r-project.org
- package.elm-lang.org
- pkgs.racket-lang.org
- clojars.org8
- luarocks.org
- galaxy.ansible.com
- artifacthub.io
- jsr.io
Binary only ships precompiled artifacts.
- central.sonatype.com
- nuget.org
- apt/dpkg
- yum/dnf
- pacman
- apk
- community.chocolatey.org
- winget-pkgs
Mixed source and binary provides source distributions plus prebuilt wheels/binaries. Native code gets platform-specific builds.
- pypi.org
- rubygems.org
- cocoapods.org
- anaconda.org
- homebrew-core9
- cache.nixos.org10
- spack11
- FreeBSD ports
- pkgsrc
Platform matrices publish multiple artifacts per release: cp39-manylinux_x86_64, cp310-macosx_arm64, etc.
- pypi.org
- rubygems.org
- anaconda.org
- cache.nixos.org
- nuget.org12
- homebrew-core
Registry governance
Who runs the registry?
Non-profit foundations operate registries as community infrastructure.
- pypi.org13
- crates.io14
- rubygems.org15
- central.sonatype.com16
- packagist.org17
- metacpan.org18
- hex.pm19
- clojars.org20
- hackage.haskell.org21
- cran.r-project.org22
- homebrew-core23
- open-vsx.org24
- artifacthub.io25
For-profit companies run registries as products or strategic infrastructure.
- npmjs.com26
- nuget.org27
- pub.dev28
- anaconda.org29
- juliahub.com30
- forge.puppet.com31
- galaxy.ansible.com32
- community.chocolatey.org33
- winget-pkgs34
- proxy.golang.org35
- deno.land36
- jsr.io36
Community projects run registries as volunteer efforts, often with fiscal sponsors.
- cocoapods.org
- conda-forge
- swiftpackageindex.com
- luarocks.org
- Carthage
- nixpkgs
Distribution projects maintain repositories as part of their distro.
Ecosystem scope
What kind of software does this package manager handle?
Language-specific registries serve a single programming language ecosystem.
- npmjs.com
- pypi.org
- rubygems.org
- crates.io
- hex.pm
- hackage.haskell.org
- metacpan.org
- clojars.org
- pub.dev
- cran.r-project.org
- juliahub.com
- package.elm-lang.org
- pkgs.racket-lang.org
- packagist.org
- proxy.golang.org
- central.sonatype.com
- luarocks.org
- jsr.io
System-level registries install operating system components and applications.
- apt/dpkg
- yum/dnf
- pacman
- apk
- homebrew-core
- nixpkgs
- Guix
- zypper
- Portage
- FreeBSD ports
- pkgsrc
- community.chocolatey.org
- winget-pkgs
Domain-specific registries serve particular use cases or industries.
- bioconductor.org
- conda-forge
- spack
- ROS
- forge.puppet.com
- registry.terraform.io
- galaxy.ansible.com
- artifacthub.io
- open-vsx.org
Version retention
Does the registry keep old versions available? What happens when a published version needs to be removed?
Keeps all versions indefinitely. You can install any historical version.
Yanking marks a version as unavailable for new installs but keeps it accessible for existing lockfiles.
- crates.io
- rubygems.org
- hex.pm
Time-limited deletion allows removal within a window, then versions become permanent.
Latest only or limited retention. Old versions disappear when new ones are published.
Registry size
How many packages? Grouped by order of magnitude.
10⁶+ (millions)
- npmjs.com
- proxy.golang.org
- pypi.org
10⁵ (hundreds of thousands)
- central.sonatype.com
- nuget.org
- packagist.org
- rubygems.org
- crates.io
- cocoapods.org
- anaconda.org
- nixpkgs
- Arch AUR
- Fedora
- Debian
- Ubuntu
10⁴ (tens of thousands)
- pub.dev
- clojars.org
- hex.pm
- hackage.haskell.org
- cran.r-project.org
- FreeBSD ports
- Alpine
10³ (thousands)
- homebrew-core
- luarocks.org
- package.elm-lang.org
Mirroring / Proxying
How hard is it to run your own registry or mirror?
Trivial means filesystem-based repos or source-host registries that need no special infrastructure.
- apt/dpkg
- yum/dnf
- proxy.golang.org
- metacpan.org
- cran.r-project.org
Supported means official tooling or documented processes exist for running mirrors or private registries.
- npmjs.com47
- pypi.org48
- central.sonatype.com49
- nuget.org
- rubygems.org
- crates.io
- packagist.org
- hex.pm50
- luarocks.org51
- clojars.org52
-
Cargo originally required cloning the full crates.io-index git repo; the sparse index now allows fetching only needed entries. ↩
-
JuliaHub has a database-backed front end but the underlying Julia General registry is a git repository. ↩
-
pkgs.racket-lang.org stores packages as files, generates a JSON index, and serves via S3. It polls git sources for updates but doesn’t use git as its storage layer. ↩
-
The AUR (Arch User Repository) is unreviewed; official repos are curated. ↩
-
Requires proving domain ownership via DNS or hosting a file at the domain. ↩
-
Elm enforces semantic versioning by diffing package APIs and rejecting publishes that break compatibility without a major version bump. ↩
-
Three-stage automated review (validator, verifier, VirusTotal scan) plus human moderation. ↩
-
Publishes JVM bytecode in JAR files, but these are built from source during the publish process. ↩
-
Bottles are prebuilt binaries for common macOS versions. ↩
-
Binary substitutes from cache.nixos.org avoid rebuilding from source. ↩
-
Spack supports binary caches but defaults to building from source. ↩
-
Runtime Identifiers (RIDs) specify platform-specific assets. ↩
-
Originally Sonatype, now Linux Foundation ↩
-
Funded by Private Packagist ↩
-
Six Colors AB, community-funded ↩
-
Fiscally sponsored by Open Source Collective ↩
-
GitHub/Microsoft ↩
-
Microsoft ↩
-
Google ↩
-
Anaconda Inc ↩
-
Julia Computing ↩
-
Perforce ↩
-
Red Hat. ↩
-
Chocolatey Software. ↩
-
Microsoft. ↩
-
Google. ↩
-
Red Hat ↩
-
Canonical ↩
-
Maven Central does not allow deletion or modification of published artifacts. ↩
-
Once cached by proxy.golang.org, modules remain available indefinitely. ↩
-
72-hour window for unpublishing, with exceptions for security issues. ↩
-
Can delete files and releases; PEP 763 proposes limiting this to 72 hours. ↩
-
Formulas point to the latest version; older versions require tapping homebrew-core history. ↩
-
Each Debian/Ubuntu release has its own repository snapshot. ↩
-
Rolling release model; only current versions are available. ↩
-
Each Alpine release has its own repository. ↩
-
devpi and Artifactory provide PyPI-compatible private registries. ↩
-
Nexus and Artifactory are widely used for hosting private Maven repositories. ↩
-
Official mirror documentation with geographic mirrors available. ↩
-
Custom rock servers can be configured via
rocks_serversin the config file. ↩ -
Mirror documentation with instructions for running your own. ↩