Skip to content
SumGuy's Ramblings
Go back

Package Management in 2026: apt, brew, nix, and the Friends We Made Along the Way

The Package Manager Proliferation Is Completely Out of Hand, and Somehow All of Them Are Correct

The situation in 2026: you have a fresh Linux machine. You want to install a development tool. Your options include apt install, brew install, nix-env -iA, flatpak install, snap install, pipx install, compiling from source, and approximately six language-specific managers (npm, pip, cargo, go install) that technically count as package managers.

This is either a rich ecosystem with mature solutions for different problems, or a fragmented nightmare with no standards. Both are true simultaneously.

The good news: once you understand what each category of tool is optimized for, the decision at any given moment becomes fairly clear. The bad news: you’ll probably end up using three of them.


apt and dpkg: The Workhorse You Take for Granted

apt (Advanced Package Tool) manages Debian and Ubuntu packages. It’s the default, the thing that’s always there, and the thing you use for anything that belongs to the operating system itself.

apt update             # Refresh package index
apt install nginx      # Install a package
apt upgrade            # Upgrade all packages
apt search keyword     # Search available packages
apt show package-name  # Package details
apt autoremove         # Remove orphaned dependencies

The underlying tool is dpkg — apt is the friendlier frontend that handles dependency resolution and repository management. dpkg -l lists installed packages; dpkg --listfiles package shows what files a package owns.

What apt is good at:

Where apt falls over:

The Debian/Ubuntu repos prioritize stability over currency. If you’re on Ubuntu LTS and want a recent version of Node.js, you’re getting Node 18 from the default repos when Node 22 has been out for years. This is a feature for servers (stability), a frustration for developers (old).

dnf and rpm: The Other Half

Red Hat / Fedora / CentOS / Rocky use dnf (formerly yum) as the package manager on top of rpm. Same concept as apt/dpkg, different format. Commands are parallel:

dnf install nginx
dnf update
dnf search keyword
rpm -qa | grep nginx  # List installed RPM packages

Fedora ships newer packages faster than Debian stable, making it more developer-friendly at the cost of some stability. RHEL (and its clones) lean even more conservative.


The Dependency Hell Problem

Here’s the fundamental issue that spawned all the alternatives.

System package managers manage one version of each library. Package A needs libssl 1.1. Package B needs libssl 3.0. You cannot install both A and B without the package manager doing something creative, and often “something creative” means “one of them doesn’t work.”

Real scenario: you’re running a Python application that requires Python 3.9, and a new tool you want requires Python 3.12. The system has Python 3.9. You upgrade. Your application breaks because it needed a deprecated API that was removed.

The classic solution was virtual environments (Python venv, Node nvm, Ruby rbenv, etc.). Create isolated environments per project, install there, don’t touch system packages. This works and is widely used. It doesn’t solve the case where you want a system-wide tool in a newer version than the distro provides.


Homebrew on Linux: User-Space Package Management

Homebrew was designed for macOS but runs perfectly well on Linux. Its key property: it installs entirely in user space (default: /home/linuxbrew/.linuxbrew) without root access. No sudo. No touching system files. No interfering with apt.

# Install Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Then
brew install node          # Latest Node.js
brew install python@3.12   # Specific Python version
brew install gh            # GitHub CLI
brew install ripgrep       # Modern grep replacement

Homebrew’s formula repository (Homebrew Core) tends to have recent software versions. It’s particularly good for developer CLI tools that aren’t available in system repos or are significantly outdated there.

Best use cases:

Limitations:


Flatpak: Sandboxed Applications

Flatpak installs GUI applications in sandboxed containers with their own dependency trees. The application brings its own libraries. No dependency conflicts. The same Flatpak package works on any Linux distribution.

# Install Flatpak (if not present)
apt install flatpak

# Add Flathub repository
flatpak remote-add --if-not-exists flathub \
  https://flathub.org/repo/flathub.flatpakrepo

# Install applications
flatpak install flathub org.videolan.VLC
flatpak install flathub com.spotify.Client
flatpak install flathub com.bitwarden.desktop

# Run
flatpak run org.videolan.VLC

# Update all
flatpak update

Flathub has become the de facto Linux app store for GUI applications. Coverage has improved dramatically — most major applications are there.

Best use cases:

Limitations:

The Snap Situation

Canonical’s Snap is a direct competitor to Flatpak. It’s also sandboxed, also distribution-independent, also bundles dependencies. The community reception has been… mixed. Snap installs are slower than Flatpak, the snap store is proprietary and controlled by Canonical, and the forced daily updates have frustrated users.

Ubuntu installs Firefox as a Snap by default, which was controversial enough to spawn guides on installing Firefox from apt instead.

Flatpak has won the community preference battle for most users who aren’t specifically on Ubuntu or committed to the Snap ecosystem.


Nix: Reproducible Everything

Nix is the package manager that makes everything before it look like toy tools. It’s also the one that requires the most explanation, has the steepest learning curve, and will consume significant time if you decide to go deep.

What Makes Nix Different

Nix is a purely functional package manager. Packages are defined as pure functions: same inputs always produce the same output. Every package version is stored in /nix/store/ with a hash in the path:

/nix/store/abc123xyz-nodejs-20.11.0/
/nix/store/def456uvw-nodejs-18.19.0/

Both versions coexist. A project that needs Node 18 uses its hash path. A different project uses Node 20. No conflicts. No virtual environments needed.

Installing Nix (on any Linux distro)

curl --proto '=https' --tlsv1.2 -sSf -L \
  https://install.determinate.systems/nix | sh -s -- install

The Determinate Systems installer is the current recommended approach (better than the official installer for most use cases).

nix-shell: Per-Project Environments

The immediate practical benefit. Drop a shell.nix in a project directory:

{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = [
    pkgs.nodejs_20
    pkgs.python311
    pkgs.postgresql_16
    pkgs.redis
  ];

  shellHook = ''
    echo "Dev environment loaded"
    export DATABASE_URL="postgresql://localhost/myapp_dev"
  '';
}

Then:

nix-shell  # Drops you into a shell with exactly these tools

Or with newer flakes syntax:

nix develop  # Uses flake.nix in current directory

The environment is isolated, reproducible, and doesn’t touch your system packages. Your colleague runs nix develop on their machine and gets the exact same versions. Works the same in CI.

home-manager: Managing Dotfiles and User Packages

home-manager extends Nix to manage your user environment — packages, dotfiles, shell configuration, all declared in Nix:

# home.nix
{ pkgs, ... }:

{
  home.packages = [
    pkgs.ripgrep
    pkgs.fd
    pkgs.bat
    pkgs.jq
    pkgs.gh
  ];

  programs.git = {
    enable = true;
    userName = "Your Name";
    userEmail = "you@example.com";
    extraConfig.init.defaultBranch = "main";
  };

  programs.zsh = {
    enable = true;
    autosuggestions.enable = true;
    syntaxHighlighting.enable = true;
  };
}

home-manager switch applies the configuration. This is your dotfiles, package list, and tool configuration all in one declarative file, version-controlled, reproducible on any machine with Nix installed.

NixOS: The Full Rabbit Hole

NixOS takes the Nix principle to the operating system level. The entire OS is configured in /etc/nixos/configuration.nix. Services, packages, users, system settings — all declarative:

# /etc/nixos/configuration.nix (excerpt)
services.nginx.enable = true;
services.postgresql.enable = true;
services.postgresql.package = pkgs.postgresql_16;

users.users.alice = {
  isNormalUser = true;
  extraGroups = [ "wheel" "docker" ];
};

environment.systemPackages = with pkgs; [
  vim git curl wget
];

nixos-rebuild switch applies changes. nixos-rebuild switch --rollback goes back. The OS is a pure function of this configuration file.

The appeal: your entire system is a git repo. Reproducible across machines. No “works on my machine” — your machine IS the configuration.

The caveat: the learning curve is real. NixOS doesn’t work like other Linux distributions. Standard Linux tutorials often don’t apply directly. The Nix language is unusual. The documentation is improving but can still be sparse in places.


Practical Developer Workflow: Using All of Them

The realistic answer for a developer in 2026:

apt          → system software, server daemons, OS tools
brew         → developer CLI tools (gh, jq, ripgrep, bat, etc.)
Flatpak      → GUI applications (VS Code from apt OR Flatpak, Spotify, Discord)
nix-shell    → per-project environments (exact versions, reproducible)
nix/home-mgr → if you decide to go deep on Nix (dotfiles, full env management)

Not every project needs nix-shell, but when you work on multiple projects with different language version requirements, it pays for itself quickly.


Comparison Table

ToolScopeRoot neededReproducibleGUI appsSystem integration
apt/dnfSystemYesMostlySomeDeep
HomebrewUser-spaceNoModerateCLI onlyLight
FlatpakApplicationsNoYesYesSandboxed
SnapApplicationsPartialPartialYesModerate
NixEverythingNo (user mode)YesVia nixpkgsConfigurable
NixOSOS-levelN/A (declarative)YesYesComplete

The Honest Recommendation

Start with apt for system stuff. Add Homebrew when you want developer tools without sudo or when you need newer versions. Use Flatpak for GUI apps, especially proprietary ones.

If you find yourself fighting dependency conflicts on a development machine, look at nix-shell for project environments. The shell.nix file approach integrates with existing workflows without committing to the full Nix stack.

If you enjoy configuration as code, want reproducibility as a first-class feature, and don’t mind an investment in learning something that’s genuinely different — explore home-manager or NixOS.

The friends we made along the way were honestly pretty useful. Even the weird functional one that speaks a language nobody else does.


Share this post on:

Previous Post
Kernel Live Patching: Security Updates Without the 3am Reboot
Next Post
DNS Over HTTPS and TLS: Encrypt Your DNS Before Your ISP Sells It