$linuxjunkies
>

An Introduction to NixOS

NixOS uses declarative configuration and atomic generations to make Linux systems reproducible and rollback-capable. Learn installation, flakes, and when it fits.

AdvancedUbuntuDebianFedoraArch12 min readUpdated June 7, 2026

Before you start

  • Comfort reading and writing structured configuration files
  • Familiarity with systemd service concepts and basic disk partitioning
  • A machine or VM with at least 20 GB of disk space (the Nix store grows over time)
  • Willingness to read Nix error messages carefully; they are verbose but precise

NixOS is not a typical Linux distribution. Where most distros configure the system through a mix of package installs, edited config files, and tribal knowledge, NixOS describes the entire system state in a single declarative configuration. The result is a system that is reproducible, rollback-capable, and surprisingly auditable. The learning curve is steep, but for the right workloads—servers that must stay consistent, developer machines that need to match CI, or systems where you want to experiment without fear—it earns its complexity.

How NixOS Works: The Core Idea

Everything on NixOS derives from the Nix language. You write a configuration file (or a set of files) describing what packages to install, what services to run, what users exist, and what their settings are. When you apply that configuration, Nix builds a complete system closure—every dependency pinned by content hash—and activates it atomically. Nothing is mutated in place; new versions live alongside old ones in /nix/store.

This produces generations: every time you rebuild the system, a new generation is recorded. The bootloader lists them. If something breaks after an update, you reboot into the previous generation. This is not a snapshot system; it does not require LVM or Btrfs. It is simply the old build still sitting in the store, pointed to by the previous generation symlink.

The store itself is immutable and content-addressed. A package path looks like /nix/store/9h7la3q…-firefox-124.0/. Two packages that differ by a single byte get different hashes and coexist without conflict.

Installing NixOS

Partition and Format

Boot the NixOS ISO. The graphical installer works fine for straightforward setups, but the manual path gives you full control. Partition your disk, create an EFI system partition if on UEFI hardware, and format:

parted /dev/sda -- mklabel gpt
parted /dev/sda -- mkpart ESP fat32 1MiB 512MiB
parted /dev/sda -- set 1 esp on
parted /dev/sda -- mkpart primary 512MiB 100%
mkfs.fat -F32 /dev/sda1
mkfs.ext4 -L nixos /dev/sda2

Mount and Generate Initial Config

mount /dev/sda2 /mnt
mkdir -p /mnt/boot
mount /dev/sda1 /mnt/boot
nixos-generate-config --root /mnt

This writes two files: /mnt/etc/nixos/hardware-configuration.nix (auto-detected hardware, regenerate if hardware changes) and /mnt/etc/nixos/configuration.nix (your editable system definition). Do not hand-edit hardware-configuration.nix unless you know exactly why.

Edit configuration.nix

Open /mnt/etc/nixos/configuration.nix in any editor available on the live ISO. A minimal working config looks like this:

{ config, pkgs, ... }:
{
  imports = [ ./hardware-configuration.nix ];

  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  networking.hostName = "myhost";
  networking.networkmanager.enable = true;

  time.timeZone = "America/New_York";
  i18n.defaultLocale = "en_US.UTF-8";

  users.users.alice = {
    isNormalUser = true;
    extraGroups = [ "wheel" "networkmanager" ];
    initialPassword = "changeme";
  };

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

  services.openssh.enable = true;

  system.stateVersion = "24.05";
}

The system.stateVersion field pins the expected state format for certain stateful services. It should reflect the NixOS release you installed with; do not bump it on upgrades unless the release notes say to.

Install

nixos-install
reboot

Nix fetches all required store paths, builds the system, and installs the bootloader. Expect several minutes on first run.

Managing the System: Generations and Rebuilds

After booting, all system management flows through nixos-rebuild. Edit /etc/nixos/configuration.nix, then apply:

# Test in a temporary boot entry without making it default
sudo nixos-rebuild test

# Build and switch immediately
sudo nixos-rebuild switch

# Build but only activate on next reboot
sudo nixos-rebuild boot

List existing generations:

sudo nix-env --list-generations --profile /nix/var/nix/profiles/system

Roll back to the previous generation immediately:

sudo nixos-rebuild switch --rollback

Or select a specific generation at the systemd-boot or GRUB menu on the next reboot. Old generations persist until you collect garbage:

# Delete generations older than 30 days
sudo nix-collect-garbage --delete-older-than 30d
sudo nixos-rebuild boot  # update bootloader entries

Nix Flakes: Pinned, Reproducible Systems

Flakes are the modern way to manage NixOS configurations. They replace the implicit reliance on the system-wide NIX_PATH with an explicit, version-locked flake.lock file. Flakes are stable in practice and widely used, though still technically marked experimental.

Enable Flakes

Add this to your configuration.nix:

nix.settings.experimental-features = [ "nix-command" "flakes" ];

Convert Your Config to a Flake

Create /etc/nixos/flake.nix:

{
  description = "My NixOS system";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
  };

  outputs = { self, nixpkgs, ... }: {
    nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [ ./configuration.nix ];
    };
  };
}

Now rebuild using the flake. The hostname in nixosConfigurations must match the machine's hostname or be specified explicitly:

sudo nixos-rebuild switch --flake /etc/nixos#myhost

Nix writes a flake.lock pinning the exact nixpkgs commit used. Commit both files to version control. To update the lock:

nix flake update /etc/nixos

This is the reproducibility payoff: two machines built from the same flake.lock get byte-for-byte identical closures for every derivation.

Home Manager: User-Level Declarative Config

Home Manager extends the same declarative model to the user environment: dotfiles, shell config, user packages. It integrates as a NixOS module or runs standalone. As a flake input:

inputs = {
  nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
  home-manager = {
    url = "github:nix-community/home-manager/release-24.05";
    inputs.nixpkgs.follows = "nixpkgs";
  };
};

Then include home-manager.nixosModules.home-manager in your modules list and configure users under home-manager.users.alice = { ... };. This is optional but powerful for developer workstations.

When to Choose NixOS

  • Servers with reproducibility requirements: staging and production can run from the same flake, same lock file, same binary cache.
  • CI/CD parity: developers' machines match the pipeline exactly.
  • Fearless experimentation: test a new service configuration, and if it breaks boot, roll back at the GRUB menu.
  • Multi-machine management: one repo, multiple nixosConfigurations entries, each with shared and host-specific modules.

NixOS is a poor fit when you need to install software from scripts that assume a FHS layout (many proprietary installers), when the team has no appetite for learning Nix, or when you need rapid package iteration that upstream Nixpkgs hasn't caught up to. The nix-ld module and tools like steam-run mitigate FHS issues, but they add friction.

Verification

After your first successful install and rebuild, confirm the system is in the expected state:

# Check current generation
sudo nix-env --list-generations --profile /nix/var/nix/profiles/system

# Confirm systemd services from config are running
systemctl status sshd
systemctl status NetworkManager

# Verify installed packages
which vim git curl

Troubleshooting

Build Fails with Evaluation Error

Nix prints the file and line number. Common causes: typo in an option name, wrong type (string where list expected), or referencing an option that doesn't exist in your nixpkgs version. Run man configuration.nix or search search.nixos.org/options to confirm valid options and types.

System Boots but Service Won't Start

Check journalctl -xeu servicename.service. The configuration was applied but the service has a runtime problem—this is the same as any systemd service failure and is unrelated to Nix itself.

Disk Fills Up

The Nix store accumulates store paths aggressively. Run sudo nix-collect-garbage -d to remove all unreferenced paths and all old generations. Schedule this periodically with nix.gc.automatic = true; nix.gc.dates = "weekly"; in your config.

Package Not in Nixpkgs

Search at search.nixos.org/packages. If genuinely absent, write a Nix derivation, use an overlay, or—for simple cases—use pkgs.stdenv.mkDerivation directly in your config. For proprietary software, check the nixpkgs-unfree attribute set after setting nixpkgs.config.allowUnfree = true;.

tested on:nixos 24.05nixos 23.11

Frequently asked questions

Can I install packages imperatively on NixOS like on other distros?
Yes, nix-env -iA nixpkgs.somepkg works, but it is discouraged because those installs are not tracked in your configuration and break the reproducibility guarantee. Prefer adding packages to environment.systemPackages or using nix shell for one-off needs.
Does NixOS work with proprietary software like NVIDIA drivers or Steam?
Yes. Set nixpkgs.config.allowUnfree = true in your config, then enable hardware.nvidia or programs.steam.enable. It works well, but some proprietary installers that assume a standard FHS layout will not work without nix-ld or a compatibility wrapper.
What is the difference between nix-channel-based NixOS and flakes?
Channels use a mutable, globally registered URL that can silently point at different nixpkgs commits on different machines. Flakes replace this with a flake.lock file that records the exact commit, making builds reproducible across machines and over time.
How does NixOS handle mutable state like databases or uploaded files?
Nix manages the software and configuration, not runtime data. Stateful directories like /var/lib/postgresql or /home are normal writable paths. The immutability applies to packages and system configuration, not to user or service data.
Is NixOS suitable for a daily-driver desktop with GNOME or KDE?
Absolutely. Set services.xserver.desktopManager.gnome.enable = true or services.desktopManager.plasma6.enable = true. Both work well, including on Wayland. Hardware-specific quirks (suspend, Bluetooth) behave the same as on any other distro.

Related guides