NixOS

1. Einführung – Das Nix-Paradigma

NixOS ist eine Linux-Distribution, die auf einem fundamental anderen Ansatz als klassische Systeme wie Ubuntu, Debian oder Arch Linux basiert. Während bei traditionellen Distributionen der Systemzustand durch eine Vielzahl von Paketverwaltungsbefehlen, Konfigurationsdateien und manuellen Eingriffen entsteht, verfolgt NixOS das Prinzip der deklarativen Systemkonfiguration: Der gesamte Zustand des Systems wird in einer einzigen Konfigurationsdatei beschrieben. Was dort steht, ist das, was installiert und aktiviert ist – nicht mehr und nicht weniger.

Das zugrundeliegende Konzept stammt aus dem Nix-Paketmanager, der ursprünglich von Eelco Dolstra im Rahmen seiner Doktorarbeit an der Universität Utrecht entwickelt wurde. Nix behandelt Pakete als unveränderliche Objekte, die anhand eines kryptografischen Hashs ihrer Eingaben (Quellcode, Abhängigkeiten, Build-Anweisungen) eindeutig identifiziert werden. Jedes Paket wird im sogenannten Nix Store unter einem Pfad wie /nix/store/abc123...-firefox-120.0/ abgelegt. Zwei Versionen desselben Pakets können problemlos nebeneinander existieren, weil sie unterschiedliche Hashes und damit unterschiedliche Pfade haben.

Daraus ergeben sich drei zentrale Eigenschaften, die NixOS von anderen Distributionen unterscheiden:

  • Deklarativ: Das gewünschte System wird beschrieben, nicht der Weg dorthin. Kein "apt install", kein "systemctl enable" von Hand – alles steht in der Konfiguration.
  • Reproduzierbar: Dieselbe Konfiguration erzeugt auf unterschiedlichen Maschinen exakt dasselbe System. Kein "bei mir funktioniert es"-Problem mehr.
  • Atomar: Systemupdates und Konfigurationsänderungen werden entweder vollständig angewendet oder gar nicht. Schlägt ein Build fehl, bleibt das laufende System unberührt. Jederzeit kann auf eine frühere Generation zurückgerollt werden.

Diese Eigenschaften machen NixOS besonders interessant für Entwickler, DevOps-Engineers und Homelab-Betreiber, die Wert auf Reproduzierbarkeit und Wartbarkeit legen.

2. Die Nix-Sprache

Nix-Konfigurationen werden in der gleichnamigen Nix-Sprache geschrieben. Es handelt sich dabei um eine rein funktionale, lazy-evaluierte, dynamisch typisierte Sprache, die ausschließlich für den Zweck der Paketkonfiguration und Systembeschreibung entworfen wurde. Wer Erfahrung mit funktionalen Sprachen wie Haskell oder Elm hat, wird schnell Parallelen erkennen – für alle anderen ist der Einstieg ungewohnt, aber nach kurzer Einarbeitungszeit gut handhabbar.

2.1 Attribute Sets

Die zentrale Datenstruktur in Nix sind Attribute Sets – vergleichbar mit Dictionaries oder JSON-Objekten. Sie bestehen aus Schlüssel-Wert-Paaren und können beliebig tief verschachtelt werden.

{
  name = "nixos-server";
  version = "1.0";
  meta = {
    description = "Mein Homelab-Server";
    maintainer = "sven";
  };
}

2.2 Listen

Listen werden mit eckigen Klammern notiert. Elemente werden durch Leerzeichen getrennt und benötigen kein Komma.

packages = [ "vim" "git" "htop" "curl" ];

ports = [ 80 443 8080 ];

2.3 let...in und with

Mit let...in können lokale Variablen innerhalb eines Ausdrucks definiert werden. Das verbessert die Lesbarkeit bei komplexeren Konfigurationen erheblich, indem wiederholte Werte zentralisiert werden.

let
  serverPort = 8080;
  serverHost = "0.0.0.0";
in {
  services.myapp = {
    port = serverPort;
    host = serverHost;
  };
}

Das Schlüsselwort with erlaubt es, alle Attribute eines Attribute Sets in den aktuellen Scope zu importieren, ohne sie einzeln zu qualifizieren. Es wird häufig bei der Paketauswahl eingesetzt, um das vorangestellte pkgs. zu vermeiden:

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

2.4 Funktionen

Funktionen in Nix nehmen genau ein Argument entgegen und werden mit einem Doppelpunkt definiert. Mehrere Argumente werden durch Currying oder über Attribute Sets als strukturiertes Argument realisiert. String-Interpolation erfolgt mit der Syntax ${ausdruck}.

# Einfache Funktion
doppelt = x: x * 2;

# Funktion mit Attribute Set als Argument und Default-Wert
begruessung = { name, sprache ? "de" }:
  if sprache == "de"
  then "Hallo, ${name}!"
  else "Hello, ${name}!";

Da Nix-Ausdrücke lazy evaluiert werden, werden Werte erst dann berechnet, wenn sie tatsächlich benötigt werden. Das ermöglicht unter anderem scheinbar zirkuläre Referenzen in Modulkonfigurationen, wie sie in der configuration.nix häufig auftreten – beispielsweise wenn ein Modul auf Optionen eines anderen Moduls verweist.

3. configuration.nix – Das Herzstück

Die Datei /etc/nixos/configuration.nix ist der zentrale Einstiegspunkt für die gesamte Systemkonfiguration. Hier wird alles beschrieben: welche Pakete installiert sind, welche Dienste laufen, wie Benutzer konfiguriert sind, wie das Netzwerk eingerichtet ist und welche Boot-Einstellungen gelten. Nach einer Änderung reicht ein einzelner Befehl, um das System in den beschriebenen Zustand zu überführen:

sudo nixos-rebuild switch

Ein typisches Beispiel für eine vollständige configuration.nix eines einfachen Servers sieht so aus:

{ config, pkgs, ... }:

{
  # Bootloader
  boot.loader.grub.enable = true;
  boot.loader.grub.device = "/dev/sda";

  # Netzwerk
  networking.hostName = "nixos-server";
  networking.networkmanager.enable = true;

  # Zeitzone und Lokalisierung
  time.timeZone = "Europe/Berlin";
  i18n.defaultLocale = "de_DE.UTF-8";

  # Systempakete
  environment.systemPackages = with pkgs; [
    vim
    git
    curl
    htop
    tmux
  ];

  # SSH-Zugriff aktivieren
  services.openssh = {
    enable = true;
    settings.PasswordAuthentication = false;
    settings.PermitRootLogin = "no";
  };

  # Benutzer anlegen
  users.users.sven = {
    isNormalUser = true;
    extraGroups = [ "wheel" "docker" ];
    openssh.authorizedKeys.keys = [
      "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... sven@laptop"
    ];
  };

  # Firewall
  networking.firewall = {
    enable = true;
    allowedTCPPorts = [ 22 80 443 ];
  };

  system.stateVersion = "24.11";
}

Was auf den ersten Blick auffällt: Es gibt keine separaten Konfigurationsdateien für SSH, keine manuellen useradd-Befehle, keine separaten Firewall-Skripte. Alles ist an einem Ort deklariert. Das System kennt seinen Sollzustand und bringt sich selbst dorthin. Entfernt man einen Dienst aus der Konfiguration und führt nixos-rebuild switch erneut aus, wird der Dienst deaktiviert und das zugehörige Paket beim nächsten Garbage-Collection-Lauf entfernt.

3.1 Modulsystem und Import-Struktur

NixOS verwendet ein leistungsfähiges Modulsystem, das es erlaubt, die Konfiguration auf mehrere Dateien aufzuteilen. Jedes Modul ist ein Nix-Ausdruck, der Optionen definieren oder setzen kann. Die Konfiguration lässt sich so sauber in logische Einheiten gliedern und wiederverwenden:

# /etc/nixos/configuration.nix
{ config, pkgs, ... }:

{
  imports = [
    ./hardware-configuration.nix
    ./modules/webserver.nix
    ./modules/database.nix
    ./modules/users.nix
  ];
}

Die Datei hardware-configuration.nix wird beim NixOS-Installer automatisch generiert und enthält hardwarespezifische Einstellungen wie Kernel-Module, Dateisysteme und Partitionen. Sie sollte in der Regel nicht manuell bearbeitet werden.

4. Nixpkgs und Kanäle

Nixpkgs ist das offizielle Paket-Repository von NixOS und gleichzeitig eines der größten und aktivsten Paket-Repositories im gesamten Open-Source-Ökosystem. Mit über 100.000 Paketen übertrifft es in der Paketanzahl viele traditionelle Distributionsrepositorys erheblich. Alle Pakete werden durch Nix-Ausdrücke beschrieben, die auf GitHub unter nixos/nixpkgs öffentlich zugänglich und einsehbar sind.

4.1 Stable vs. Unstable Channel

Nixpkgs wird in verschiedenen Kanälen (Channels) bereitgestellt. Die wichtigsten sind:

  • nixos-24.05 / nixos-24.11 (stable): Halbjährliche Releases mit getesteten, stabilen Paketen. Empfohlen für Server und produktive Systeme, bei denen Stabilität Priorität hat.
  • nixos-unstable: Aktuellste Pakete, direkt aus dem Hauptentwicklungszweig. Gut für Desktops und Entwicklungsumgebungen, wo aktuelle Software wichtiger ist als maximale Stabilität.

Der aktuell verwendete Kanal lässt sich mit folgendem Befehl anzeigen und wechseln:

# Aktuellen Kanal anzeigen
sudo nix-channel --list

# Auf unstable wechseln
sudo nix-channel --add https://nixos.org/channels/nixos-unstable nixos
sudo nix-channel --update

# Danach System neu bauen
sudo nixos-rebuild switch

4.2 nix-env vs. deklarative Konfiguration

Nix bietet mit nix-env auch die Möglichkeit, Pakete imperativ für einzelne Benutzer zu installieren – ähnlich wie apt install. Für NixOS wird dieser Ansatz jedoch ausdrücklich nicht empfohlen. Er untergräbt die Reproduzierbarkeit des Systems, weil der tatsächliche Systemzustand dann nicht mehr vollständig aus der configuration.nix ableitbar ist. Die deklarative Verwaltung über environment.systemPackages oder den Home Manager für benutzerspezifische Pakete ist der bevorzugte und konsistente Weg.

5. Nix Flakes

Flakes sind eine neuere, experimentelle – inzwischen jedoch weit verbreitete und aktiv genutzte – Erweiterung des Nix-Ökosystems, die Reproduzierbarkeit auf eine neue Ebene hebt. Sie lösen ein grundlegendes Problem der klassischen Nix-Kanäle: Auch wenn zwei Systeme denselben Kanal verwenden, können sie unterschiedliche Paketversionen erhalten, weil der Kanal sich zwischen den jeweiligen Zeitpunkten der letzten Aktualisierung verändert haben kann.

Flakes führen eine flake.lock-Datei ein – konzeptuell vergleichbar mit package-lock.json in Node.js oder Cargo.lock in Rust – die alle Eingaben (Inputs) mit exakten Git-Commits fixiert. Dadurch ist garantiert, dass dasselbe Flake auf jeder Maschine und zu jedem Zeitpunkt exakt dasselbe Ergebnis liefert.

5.1 Struktur einer flake.nix

Eine flake.nix besteht aus drei Hauptbereichen: einer Beschreibung (description), den Eingaben (inputs) und den Ausgaben (outputs).

{
  description = "Meine NixOS-Konfiguration";

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

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

Um Flakes zu aktivieren, muss zunächst das experimentelle Feature in der Konfiguration eingeschaltet werden:

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

Anschließend kann das System mit folgendem Befehl gebaut werden, wobei nixos-server dem Namen in nixosConfigurations entspricht:

sudo nixos-rebuild switch --flake .#nixos-server

Um alle Inputs auf den jeweils neuesten Stand gemäß der definierten Referenz zu aktualisieren und die Lockfile neu zu schreiben, genügt:

nix flake update

5.2 Vorteile von Flakes im Überblick

  • Vollständig reproduzierbare Builds durch fixierte Inputs in der Lockfile
  • Klare, standardisierte Projektstruktur für Nix-Projekte
  • Einfaches Teilen und Wiederverwenden von Konfigurationen über Git-Repositories
  • Lockfile ermöglicht kontrollierte, gezielte Updates einzelner Abhängigkeiten
  • Grundlage für moderne NixOS-Workflows und Community-Projekte wie Home Manager, NixOS-Hardware und Agenix

6. Homelab-Einsatz

NixOS ist eine ausgezeichnete Wahl für den Betrieb eines Homelabs. Die deklarative Natur der Konfiguration bedeutet, dass ein Server vollständig aus einer Textdatei wiederhergestellt werden kann – kein mühsames Dokumentieren von Setup-Schritten, kein "Was hatte ich nochmal installiert?"-Problem. Dienste wie Nginx, PostgreSQL oder Docker lassen sich mit wenigen Zeilen sauber konfigurieren und sind durch Versionskontrolle vollständig nachvollziehbar.

6.1 Nginx als Reverse Proxy mit automatischem TLS

services.nginx = {
  enable = true;
  recommendedProxySettings = true;
  recommendedTlsSettings = true;
  recommendedGzipSettings = true;
  recommendedOptimisation = true;

  virtualHosts."app.example.com" = {
    forceSSL = true;
    enableACME = true;
    locations."/" = {
      proxyPass = "http://127.0.0.1:3000";
      proxyWebsockets = true;
    };
  };
};

# Let's Encrypt Zertifikate automatisch verwalten
security.acme = {
  acceptTerms = true;
  defaults.email = "admin@example.com";
};

6.2 PostgreSQL-Datenbankserver

services.postgresql = {
  enable = true;
  package = pkgs.postgresql_16;
  enableTCPIP = false;
  authentication = pkgs.lib.mkOverride 10 ''
    local all all trust
    host  all all 127.0.0.1/32 scram-sha-256
  '';
  initialScript = pkgs.writeText "init-db.sql" ''
    CREATE USER appuser WITH PASSWORD 'geheimespasswort';
    CREATE DATABASE appdb OWNER appuser;
  '';
};

6.3 Docker und Container-Verwaltung

Auch wenn NixOS selbst Alternativen wie OCI-Container über virtualisation.oci-containers oder Podman bietet, ist Docker im Homelab weit verbreitet und lässt sich einfach aktivieren:

virtualisation.docker = {
  enable = true;
  autoPrune = {
    enable = true;
    dates = "weekly";
  };
};

# OCI-Container deklarativ verwalten (ohne docker-compose)
virtualisation.oci-containers.containers = {
  portainer = {
    image = "portainer/portainer-ce:latest";
    ports = [ "9000:9000" "9443:9443" ];
    volumes = [
      "/var/run/docker.sock:/var/run/docker.sock"
      "portainer_data:/data"
    ];
    autoStart = true;
  };
};

6.4 Automatische Updates und Generationenverwaltung

Einer der größten Vorteile von NixOS im Homelab ist das atomare Update-Modell mit Generationenverwaltung. Jedes Mal, wenn nixos-rebuild switch erfolgreich ausgeführt wird, entsteht eine neue Systemgeneration. Alle Generationen bleiben im Bootloader sichtbar und können jederzeit aktiviert werden:

# Verfügbare Generationen anzeigen
sudo nix-env --list-generations --profile /nix/var/nix/profiles/system

# Auf die vorherige Generation zurückrollen
sudo nixos-rebuild switch --rollback

# Alten Nix Store bereinigen (Generationen älter als 14 Tage)
sudo nix-collect-garbage --delete-older-than 14d

Für automatische Systemupdates im Hintergrund bietet NixOS einen integrierten Mechanismus, der per Systemd-Timer gesteuert wird:

system.autoUpgrade = {
  enable = true;
  allowReboot = false;
  flake = "github:meinnutzer/nixos-config#nixos-server";
  flags = [ "--update-input" "nixpkgs" ];
  dates = "04:00";
};

Schlägt ein Update fehl oder verhält sich ein Dienst nach einem Update unerwartet, ist ein Rollback auf die vorherige Generation in Sekunden möglich – entweder über den Bootloader beim nächsten Neustart oder direkt im laufenden Betrieb über nixos-rebuild switch --rollback. Das ist ein erheblicher Vorteil gegenüber traditionellen Linux-Systemen, bei denen ein fehlgeschlagenes Update aufwendige manuelle Reparaturarbeit bedeuten kann.

7. Vor- und Nachteile

NixOS ist kein universell überlegenes Betriebssystem – es ist ein Werkzeug mit klaren Stärken und ebenso klaren Schwächen. Eine ehrliche Einschätzung hilft dabei, zu beurteilen, ob NixOS das richtige System für den eigenen Anwendungsfall ist.

7.1 Vorteile

  • Perfekte Reproduzierbarkeit: Dieselbe configuration.nix (oder flake.nix) erzeugt immer dasselbe System. Kein manuelles Nachbauen, kein Dokumentieren von Setup-Schritten. Das System ist selbst die Dokumentation.
  • Atomare Updates mit Rollback: Kein halbfertig aktualisiertes System, kein "broken state". Rollbacks auf jede frühere Generation sind ohne Datenverlust möglich, direkt aus dem Bootloader heraus.
  • Saubere Abhängigkeitsverwaltung: Paketabhängigkeiten werden isoliert im Nix Store verwaltet. Verschiedene Projekte können unterschiedliche Versionen derselben Bibliothek nutzen, ohne Konflikte zu verursachen.
  • Riesiges Paket-Repository: Nixpkgs ist mit über 100.000 Paketen eines der umfangreichsten Repositories überhaupt und wird aktiv gepflegt.
  • Hervorragend für Homelab und Server: Einmal als Code dokumentiert, immer reproduzierbar. Neue Server können aus der gleichen Konfiguration hochgezogen werden. Infrastructure as Code ohne zusätzliches Tooling.
  • Nix Dev Shells: Mit nix develop und einer devShell-Definition in der flake.nix lassen sich isolierte Entwicklungsumgebungen pro Projekt definieren – ohne Virtualisierung oder Docker.
  • Home Manager: Das Community-Projekt Home Manager ermöglicht es, auch die Benutzerkonfiguration (dotfiles, Anwendungseinstellungen) deklarativ zu verwalten.

7.2 Nachteile und Einschränkungen

  • Steile Lernkurve: Die Nix-Sprache, das Modulsystem, das Konzept des Nix Stores und die Unterschiede zwischen klassischem und Flake-basiertem Workflow sind für Einsteiger erheblich ungewohnt. Fehlermeldungen können kryptisch sein und erfordern oft ein gutes Verständnis des Evaluierungsprozesses.
  • Schlechte FHS-Kompatibilität: NixOS folgt nicht dem Filesystem Hierarchy Standard (FHS). Binaries, die bestimmte Bibliothekspfade wie /lib/x86_64-linux-gnu/ oder /usr/lib/ erwarten, funktionieren ohne Anpassung nicht. Vorgefertigte Binaries aus dem Internet – AppImages, proprietäre Installationsskripte oder manuelle Tarballs – müssen häufig mit patchelf, nix-ld oder einem FHS-kompatiblen Container angepasst werden.
  • Proprietäre Treiber: NVIDIA-Treiber und andere proprietäre Treiber können zwar konfiguriert werden (services.xserver.videoDrivers = [ "nvidia" ];), erfordern aber explizite Konfiguration und können bei Kernel-Updates oder nach Distributionswechseln gelegentlich Probleme bereiten.
  • Hoher Speicherverbrauch des Nix Stores: Da alte Generationen und alle Pakete im Nix Store verbleiben, kann der belegte Speicherplatz schnell wachsen. Regelmäßiges Garbage Collection (nix-collect-garbage -d) ist notwendig, um nicht mehr benötigte Pakete und Generationen zu entfernen.
  • Ökosystem-Fragmentierung: Mit der Einführung von Flakes hat die Community zwei teilweise inkompatible Workflows. Viele Tutorials und Anleitungen im Internet sind noch auf das klassische Channel-System ausgerichtet, was Einsteiger verwirren kann. Es ist nicht immer klar, welchem Ansatz man bei der Suche nach Lösungen folgen soll.
  • Nicht für jeden Anwendungsfall geeignet: Als Desktop-System für weniger technisch versierte Nutzer ist NixOS nur bedingt empfehlenswert. Selbst einfache Aufgaben wie die Installation einer nicht im Repository enthaltenen Software erfordern ein grundlegendes Verständnis des Systems.

Für technisch versierte Anwender, Homelab-Betreiber und alle, die Wert auf Reproduzierbarkeit, Nachvollziehbarkeit und langfristige Wartbarkeit ihrer Infrastruktur legen, ist NixOS jedoch eine der interessantesten und mächtigsten Optionen im gesamten Linux-Ökosystem. Der initiale Lernaufwand zahlt sich spätestens dann aus, wenn ein Server nach einem Hardwareausfall vollständig aus einer einzigen Konfigurationsdatei wiederhergestellt werden kann – oder wenn ein fehlerhaftes Update in unter einer Minute rückgängig gemacht ist, ohne dass das laufende System je in einen inkonsistenten Zustand geraten ist.