Nix on Ubuntu and Debian: Multi-Version Software and Atomic Rollbacks

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Escaping Dependency Hell

Standard tools like APT are great for system stability. However, they struggle when you need to run three different versions of Node.js or test a new package without cluttering your global filesystem. On my 4GB RAM Ubuntu 22.04 VPS, I found that Nix reduced my deployment overhead significantly. Instead of spinning up five separate Docker containers for minor utilities—each eating 300MB of RAM—I now run them natively and isolated via Nix.

How Nix Shifts the Paradigm

To understand why Nix is a fundamental shift for Debian-based systems, we have to look at how it handles files compared to the tools we use every day.

1. Nix vs. APT

APT installs files into shared directories like /usr/bin. This creates a global environment where upgrading a single Python library might accidentally break three other applications. Nix avoids this by storing every package in a unique subdirectory within /nix/store. These directory names include a unique cryptographic hash. Because of this, you can have Python 3.8, 3.10, and 3.12 installed simultaneously without them ever knowing the others exist.

2. Nix vs. Docker

Docker provides isolation through containers, but this comes with a heavy tax on disk space and startup time. A basic Ubuntu Docker image might take 80MB before you even install a tool. Nix provides package-level isolation with the performance of a native binary. I use it for development environments where I need specific C++ compilers. It gives me the reproducibility of a container but without the 2GB image overhead.

The Reality of Using Nix

You should weigh the practical benefits against the learning curve before moving your entire workflow over.

The Advantages

  • Atomic Upgrades: Nix never overwrites files during an update. It simply points a new symbolic link to the new version. If an update fails, your system remains exactly as it was.
  • Zero Sudo Required: Once the initial setup is complete, any user can install software. This is a massive win for shared servers where you don’t have root access but need a specific version of ripgrep.
  • Portable Environments: You can drop a shell.nix file into a Git repo. When a teammate clones it, they run one command and get the exact same toolchain you used.

The Trade-offs

  • Disk Consumption: Isolation isn’t free. Because Nix keeps multiple versions of dependencies, your /nix folder can easily swell to 20GB or 30GB after a few weeks of experimentation.
  • New Language: You’ll need to learn the Nix expression language. It is a functional language, which feels alien if you are used to writing standard Bash or Python scripts.

Setting Up on Ubuntu and Debian

I recommend the Multi-user Installation for most production setups. It creates a dedicated nixbld group and several build users. This setup improves security and lets multiple users share the Nix store while keeping their own profiles private.

Check your storage first. Ensure you have at least 15GB of free space on your root partition. If you’re tight on space, mount a dedicated SSD or a separate volume at /nix before you start the installation.

Implementation Guide

1. Installation

The official multi-user script is the most reliable path for modern Debian systems. It automatically configures the necessary systemd units for you.

sh <(curl -L https://nixos.org/nix/install) --daemon

Once the installer finishes, refresh your environment variables by sourcing the Nix profile:

. /etc/profile.d/nix.sh

2. Finding and Installing Packages

Nix pulls from a massive repository called nixpkgs, which currently hosts over 80,000 packages. To find a specific tool like htop, use the search command:

nix-env -qaP htop

To install a package to your user profile without touching system files:

nix-env -iA nixpkgs.htop

3. Instant Rollbacks

Every change you make creates a new “generation.” If you install a new version of a tool and it breaks your scripts, you can view your history immediately:

nix-env --list-generations

To jump back to the previous working state, just run:

nix-env --rollback

4. Using nix-shell for Temporary Tools

This is arguably the best feature for developers. Imagine you need to run a Python script that requires Python 3.11 and the requests library, but you don’t want them on your system permanently. You can enter a temporary shell:

nix-shell -p python311 python311Packages.requests

The moment you type exit, those packages vanish from your PATH. Your system stays clean, yet you had exactly what you needed for that one task.

Keeping the System Lean

Nix doesn’t delete old versions automatically. If you don’t clean up, you’ll eventually run out of disk space. I’ve seen servers grind to a halt because the Nix store hit 100% disk usage.

Garbage Collection

To clear out packages that aren’t being used by any active generation, run:

nix-collect-garbage

To be more thorough and delete all old generations (freeing up the most space):

nix-collect-garbage -d

Final Strategy

Running Nix alongside APT offers a hybrid approach to Linux management. I use APT for core system essentials like the kernel, SSH, and systemd. For everything else—from CLI tools to complex development runtimes—I use Nix. This separation keeps the base OS stable while giving me total flexibility for my applications. If you’re tired of PPA conflicts or broken dependencies, Nix is the logical next step for your workflow.

Share: