In meinem letzten Beitrag der NixOS-Reihe zeige ich, wie man einen einzelnen Rechner aus dem gemeinsamen Setup herauslöst und individuell konfiguriert – ohne die anderen Rechner auch nur zu berühren.
Wer meiner Reihe gefolgt ist, hat mittlerweile vielleicht mehrere Rechner mit identischer Konfiguration am Laufen. Eine configuration.nix für alle, eine home.nix für alle – sauber, simpel, reproduzierbar. Das ist bereits beeindruckend und für viele Anwendungsfälle völlig ausreichend.
Aber die Realität ist eben manchmal: drei Rechner, drei unterschiedliche Charaktere.
Es gibt viele Gründe und Szenarien für eine abweichende Konfiguration - als Beispiel nehme ich hier einfach mal meine Situation - das Vorgehen ist aber exemplarisch und vollständig auf andere Szenarien übertragbar.
Rücksicht nehmen muss ich auf einen geliebten alten Wegbegleiter, ein alter Lenovo mit nur 8 GB und einem schwachen Pentium Prozessor. Er ist das schwächste Glied in meiner kleinen Flotte und leidet unter der identischen Konfiguration, die meine leistungsfähigen Dell-Rechner problemlos stemmen. Fotoverwaltung mit tausenden RAW-Dateien - oder gar deren Entwicklung? Auf dem Lenovo undenkbar. Synchronisation mit mehreren Cloud-Speichern im Hintergrund? Verschwendete Ressourcen. Und der LanguageTool-Server mit seiner Java-Implementierung, die gerne mal fast 2 GB RAM schluckt – den will ich auf dem schwächsten Rechner erst recht nicht.
Dazu kommt: Der Lenovo ist ein Detachable, also ein Gerät das ich meist als Tablet weitab einer Steckdose nutze. Da möchte ich einen Energieverwaltungs-Button der wirklich funktioniert - manchmal muss ich die volle Power nämlich auch im Akkubetrieb zuschalten können. Mit TLP ist der Energieverwaltungs-Button bekanntlich wirkungslos, weil absichtlich lahm gelegt. TLP übernimmt nämlich die Kontrolle vollautomatisch über die CPU-Taktung und ignoriert was ich z.B. über GNOME einstellen möchte.
Jetzt könnte man auf die Idee kommen, die configuration.nix mit Bedingungen zu pflastern: "wenn Lenovo, dann dies, sonst das". Das ist aber der Weg in die Unübersichtlichkeit - und Unübersichtlichkeit ist nicht "Nix-like", wie du dir anhand des bisher Dargestellten sicher denken kannst. NixOS hat eine elegantere Antwort parat.
Das Prinzip: additive Module
In NixOS lassen sich mehrere Konfigurationsdateien zu einem System zusammensetzen. Statt eine einzige große configuration.nix mit Ausnahmen vollzustopfen, addiert man für den Lenovo einfach eine weitere kleine Datei obendrauf. Die configuration.nix bleibt vollständig unangetastet. Die Dell-Rechner bemerken von alldem nichts.
Das Ergebnis sieht so aus:
/etc/nixos/
├── flake.nix ← angepasst
├── configuration.nix ← unverändert (gilt für alle)
├── home.nix ← unverändert (gilt für meine zwei Dell)
├── lenovo-system.nix ← NEU: Lenovo-spezifisch (System)
├── home-lenovo.nix ← NEU: Lenovo-spezifisch (Benutzer)
├── hardware-configuration-dell5320.nix
├── hardware-configuration-dellpro.nix
└── hardware-configuration-lenovo.nix
Zwei neue Dateien. Mehr nicht.
Schritt 1: lenovo-system.nix erstellen
Diese Datei enthält ausschließlich die Dinge, die auf dem Lenovo anders sein sollen als in der configuration.nix. Alles andere – GNOME, Drucker, Sprache, Pakete – bleibt unverändert aus der gemeinsamen Konfiguration.
Die folgenden Dateien spiegeln meine eigene Konfiguration wider, sind also nur als Beispiele zu sehen. Das Grundprinzip sollte dir dadurch aber klar werden und dich zu einer individuellen Umsetzung auf deine Rahmenbedingungen befähigen.
Ich habe mich entschlossen meine Konfigurationsdateien weitgehend ungekürzt hier wiederzugeben. Das ist für die Lesbarkeit des Artikels zwar ungünstig, aber hilfreich für alle, welche damals meine Setup-Dateien als Ausgangspunkt für die eigene Konfiguration genutzt haben.
sudo nano /etc/nixos/lenovo-system.nix
# ============================================================
# Lenovo-spezifische Systemkonfiguration
# Ergänzt und überschreibt configuration.nix nur dort,
# wo der Lenovo andere Einstellungen benötigt.
# ============================================================
{ config, pkgs, lib, ... }:
{
# ─────────────────────────────────────────
# TLP deaktivieren – es überschreibt den GNOME-Energieprofil-Button.
# lib.mkForce ist nötig um den Wert aus configuration.nix zu überschreiben.
# ─────────────────────────────────────────
services.tlp.enable = lib.mkForce false;
# ─────────────────────────────────────────
# Power Profiles Daemon aktivieren – er steuert die CPU-Leistungsstufen
# und integriert sich vollständig in GNOME. Der Energieprofil-Button
# oben rechts in GNOME funktioniert damit wieder.
# ─────────────────────────────────────────
services.power-profiles-daemon.enable = lib.mkForce true;
# ─────────────────────────────────────────
# Intel Thermaldienst
# Schützt die CPU vor Überhitzung auf schwacher Hardware.
# ─────────────────────────────────────────
services.thermald.enable = true;
}
Wie wirkt hier lib.mkForce? Wenn zwei NixOS-Module dieselbe Option setzen, gibt es normalerweise einen Konflikt. lib.mkForce sagt NixOS: "Diese Datei gewinnt – egal was andere Module sagen." Das ist genau das richtige Werkzeug um Einstellungen aus der gemeinsamen configuration.nix für einen einzelnen Rechner zu überschreiben.
Schritt 2: home-lenovo.nix erstellen
Hier liegt der eigentliche Hebel für die Ressourcenschonung. Statt die gemeinsame home.nix mit Ausnahmen zu belasten, erstellen wir eine eigene, schlanke Variante.
Wie gesagt, hier nochmal meine (fast) vollständige originale home.nix mit allen Skripten - jetzt ergänzt mit Markierungen zu den Stellen, die sich geändert haben und den entsprechenden Auskommentierungen.
sudo nano /etc/nixos/home-lenovo.nix
# ============================================================
# Home Manager Konfiguration – Lenovo)
# Schlanke Version ohne LanguageTool, Fotosoftware und Clouds.
# ============================================================
{ config, pkgs, pkgs-unstable, ... }:
{
home.username = "dein-username"; # anpassen!
home.homeDirectory = "/home/dein-username"; # anpassen!
# Nie die Erstinstallations-Version ändern, auch nicht nach Versionssprung!
home.stateVersion = "25.11";
programs.home-manager.enable = true;
# ─────────────────────────────────────────
# Benutzerprogramme – schlanke Auswahl
# ─────────────────────────────────────────
home.packages = [
# ── Büro (unstable) ───────────────────
pkgs-unstable.libreoffice-fresh # Office-Suite
pkgs-unstable.hunspell # Rechtschreibprüfung
pkgs-unstable.hunspellDicts.de_DE # Deutsches Wörterbuch
pkgs-unstable.mythes # Thesaurus
# languagetool ← bewusst weggelassen: Java-Server, ~2 GB RAM
# ── Kommunikation (unstable) ──────────
pkgs-unstable.thunderbird # E-Mail
pkgs-unstable.nextcloud-client
# weitere Clouddienste ← nicht benötigt
# ── Notizen (unstable) ────────────────
pkgs-unstable.obsidian # Markdown-Notizen (unfree)
# ── Datensicherung (unstable) ─────────
# pkgs-unstable.pika-backup ← bewusst weggelassen
# ── Fotografie ────────────────────────
# pkgs-unstable.rapidraw ← nicht benötigt
# pkgs-unstable.digikam ← nicht benötigt
# ── Shell & Tools (stable) ────────────
pkgs.starship
pkgs.nix-output-monitor
pkgs.nvd
];
# LanguageTool-Dienst bewusst nicht gestartet
# (spart ~2 GB RAM auf schwacher Hardware)
# ─────────────────────────────────────────
# Git – Benutzerkonfiguration
# ─────────────────────────────────────────
programs.git = {
enable = true;
settings = {
user.email = "deine.mail@für.codeberg"; # anpassen!
user.name = "dein-username"; # anpassen!
};
};
# ─────────────────────────────────────────
# SSH – Konfiguration für Codeberg
# ─────────────────────────────────────────
programs.ssh = {
enable = true;
enableDefaultConfig = false;
matchBlocks."codeberg.org" = {
hostname = "codeberg.org";
user = "git";
identityFile = "~/.ssh/id_ed25519";
};
};
# ─────────────────────────────────────────
# GTK-Theme (Arc)
# ─────────────────────────────────────────
gtk = {
enable = true;
theme = { name = "Arc"; package = pkgs.arc-theme; };
iconTheme = { name = "Papirus"; package = pkgs.papirus-icon-theme; };
cursorTheme = { name = "Adwaita"; size = 24; };
};
# ─────────────────────────────────────────
# XDG User Directories – Ordner auf Deutsch
# ─────────────────────────────────────────
xdg.userDirs = {
enable = true;
createDirectories = true;
desktop = "${config.home.homeDirectory}/Schreibtisch";
documents = "${config.home.homeDirectory}/Dokumente";
download = "${config.home.homeDirectory}/Downloads";
music = "${config.home.homeDirectory}/Musik";
pictures = "${config.home.homeDirectory}/Bilder";
publicShare = "${config.home.homeDirectory}/Öffentlich";
templates = "${config.home.homeDirectory}/Vorlagen";
videos = "${config.home.homeDirectory}/Videos";
};
# ─────────────────────────────────────────
# Fish Shell
# ─────────────────────────────────────────
programs.fish = {
enable = true;
interactiveShellInit = ''
starship init fish | source
'';
shellAliases = {
".." = "cd ..";
"..." = "cd ../..";
ll = "ls -lah";
nix-clean-all = "sudo nix-collect-garbage -d";
};
functions = {
rebuild = {
description = "System auf diesem Rechner neu bauen";
body = ''
sudo nixos-rebuild switch --flake /etc/nixos#(hostname) 2>&1 | nom
'';
};
update-push = {
description = "NixOS aktualisieren und auf Codeberg sichern";
body = ''
echo "────────────────────────────────────────────"
echo " NixOS Update + Store bereinigen + Git Push"
echo "────────────────────────────────────────────"
cd /etc/nixos
if test -e result
sudo rm -f result
end
if test -f .gitignore
grep -q '^result$' .gitignore
or echo "result" | sudo tee -a .gitignore >/dev/null
else
echo "result" | sudo tee .gitignore >/dev/null
end
sudo git rm -r --cached result 2>/dev/null
or true
echo ""
echo "→ Schritt 1/5: flake.lock aktualisieren..."
sudo nix flake update
or return 1
echo ""
echo "→ Schritt 2/5: System bauen und aktivieren..."
set old_system (readlink -f /run/current-system)
sudo nixos-rebuild switch --flake /etc/nixos#(hostname) 2>&1 | nom
if test $pipestatus[1] -ne 0
echo "✗ Fehler beim Rebuild."
return 1
end
echo ""
echo "→ Schritt 3/5: Paketänderungen anzeigen..."
nvd diff $old_system /run/current-system
echo ""
echo "→ Schritt 4/5: Nix-Store bereinigen..."
sudo nix-collect-garbage --delete-older-than 30d
or return 1
echo ""
echo "→ Schritt 5/5: Auf Codeberg sichern..."
sudo git add -A
sudo git commit -m "System aktualisiert "(date +%Y-%m-%d)
or true
sudo git push origin master
and begin
echo "✓ Fertig!"
return 0
end
echo "✗ Push fehlgeschlagen. Bitte später erneut versuchen."
return 1
'';
};
pull-update = {
description = "Konfiguration von Codeberg holen und System bauen";
body = ''
echo "──────────────────────────────────────"
echo " Git Pull + NixOS Rebuild + Cleanup "
echo "──────────────────────────────────────"
cd /etc/nixos
if test -e result
sudo rm -f result
end
if test -f .gitignore
grep -q '^result$' .gitignore
or echo "result" | sudo tee -a .gitignore >/dev/null
else
echo "result" | sudo tee .gitignore >/dev/null
end
sudo git rm -r --cached result 2>/dev/null
or true
echo ""
echo "→ Schritt 1/4: Konfiguration von Codeberg holen..."
sudo git fetch origin
or return 1
sudo git reset --hard origin/master
or return 1
echo ""
echo "→ Schritt 2/4: System bauen und aktivieren..."
set old_system (readlink -f /run/current-system)
sudo nixos-rebuild switch --flake /etc/nixos#(hostname) 2>&1 | nom
if test $pipestatus[1] -ne 0
echo "✗ Fehler beim Rebuild."
return 1
end
echo ""
echo "→ Schritt 3/4: Paketänderungen anzeigen..."
nvd diff $old_system /run/current-system
echo ""
echo "→ Schritt 4/4: Nix-Store bereinigen..."
sudo nix-collect-garbage --delete-older-than 30d
or return 1
echo ""
echo "✓ Fertig!"
'';
};
};
};
# ─────────────────────────────────────────
# GNOME-Einstellungen via dconf
# ─────────────────────────────────────────
dconf.settings = {
"org/gnome/desktop/interface" = {
color-scheme = "default";
cursor-theme = "Adwaita";
};
"org/gnome/desktop/wm/preferences" = {
button-layout = "appmenu:minimize,maximize,close";
};
"org/gnome/settings-daemon/plugins/color" = {
night-light-enabled = false;
night-light-schedule-automatic = false;
night-light-schedule-from = 22.0;
night-light-schedule-to = 7.0;
night-light-temperature = 4000;
};
"org/gnome/desktop/peripherals/touchpad" = {
tap-to-click = true;
natural-scroll = true;
};
"org/gnome/mutter" = {
experimental-features = [ "scale-monitor-framebuffer" ];
};
"org/gnome/shell" = {
disable-user-extensions = false;
enabled-extensions = [
pkgs.gnomeExtensions.appindicator.extensionUuid
"dash-to-dock@micxgx.gmail.com"
"no-overview@fthx"
];
};
};
}
Schritt 3: flake.nix anpassen
Das ist der entscheidende Schritt. Bisher zeigt die flake.nix für alle Rechner auf dieselbe home.nix. Das muss für den Lenovo geändert werden – und gleichzeitig muss lenovo-system.nix als zusätzliches Modul eingebunden werden.
Dazu wandert home-manager.users.dein-username aus den gemeinsamen Modulen heraus und wird stattdessen je Rechner einzeln eingetragen. Bitte lies alle Kommentierungen in der Datei, damit müsste es klar werden.
Die flake.nix hier nur als Ausschnitt. Bitte beachten: ich habe überall meinen Usernamen und die Hostnamen meiner Rechner depersonalisiert - das sind viele Stellen an denen nun entweder "dein-username" oder "dein-hostname" steht - bitte detektivisch genau aufspüren.
sudo nano /etc/nixos/flake.nix
{
outputs = { self, nixpkgs, nixpkgs-unstable, home-manager, ... }:
let
system = "x86_64-linux";
pkgs-unstable = import nixpkgs-unstable {
inherit system;
config.allowUnfree = true;
};
# Gemeinsame Basis – gilt für alle Rechner.
# home-manager.users.dein-username ist hier bewusst NICHT mehr enthalten,
# da jeder Rechner seine eigene home.nix bekommt.
common-modules = [
./configuration.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.backupFileExtension = "backup";
home-manager.extraSpecialArgs = { inherit pkgs-unstable; };
# ← home-manager.users.dein-username wurde hier entfernt
}
];
in {
nixosConfigurations = {
# Dell 5320 – unverändert, nutzt die gemeinsame home.nix
dein-hostname = nixpkgs.lib.nixosSystem {
inherit system;
specialArgs = { inherit pkgs-unstable; };
modules = common-modules ++ [
./hardware-configuration.nix
{ networking.hostName = "dein-hostname"; }
{ home-manager.users.dein-username = import ./home.nix; } # gemeinsame home.nix
];
};
# Dell Pro Plus – unverändert, nutzt die gemeinsame home.nix
dein-hostname = nixpkgs.lib.nixosSystem {
inherit system;
specialArgs = { inherit pkgs-unstable; };
modules = common-modules ++ [
./hardware-configuration-dein-hostname.nix
{ networking.hostName = "dein-hostname"; }
{ home-manager.users.dein-username = import ./home.nix; } # gemeinsame home.nix
];
};
# Lenovo – individuelle Konfiguration:
# lenovo-system.nix überschreibt ausgewählte Systemeinstellungen,
# home-lenovo.nix ersetzt die Benutzerumgebung komplett.
lenovo = nixpkgs.lib.nixosSystem {
inherit system;
specialArgs = { inherit pkgs-unstable; };
modules = common-modules ++ [
./hardware-configuration-lenovo.nix
{ networking.hostName = "dein-hostname"; }
./lenovo-system.nix # ← Systemoverrides
{ home-manager.users.dein-username = import ./home-lenovo.nix; } # ← schlanke home.nix
];
};
};
};
}
Schritt 4: Alle Dateien Git bekannt machen und bauen
Flakes ignorieren Dateien die Git nicht kennt – das kennen wir bereits. Daher erst registrieren:
cd /etc/nixos
sudo git add lenovo-system.nix home-lenovo.nix flake.nix
sudo git commit -m "Lenovo individualisiert: schlanke home.nix, Power Profiles Daemon"
Dann auf dem Lenovo einmalig manuell bauen:
sudo nixos-rebuild switch --flake /etc/nixos#lenovo
Und auf Codeberg sichern:
sudo git push origin master
Ab jetzt läuft auf dem Lenovo update-push und pull-update (direkt einmal danach ausführen) wie gewohnt.
Meine deklarativ gesetzte Wunsch-Konfiguration
|
Dell 5320 |
Dell Pro Plus |
Lenovo |
| Systemkonfiguration |
configuration.nix |
configuration.nix |
configuration.nix + lenovo-system.nix |
| Benutzerumgebung |
home.nix |
home.nix |
home-lenovo.nix |
| Energieverwaltung |
TLP |
TLP |
Power Profiles Daemon ✅ |
| GNOME-Energieprofil-Button |
wirkungslos |
wirkungslos |
funktioniert ✅ |
| LanguageTool (~2 GB RAM) |
✅ läuft |
✅ läuft |
❌ gespart |
| Fotosoftware |
✅ |
✅ |
❌ nicht nötig |
| mehrere Cloud-Dienste synchronisieren |
✅ |
✅ |
❌ nicht nötig |
Die Dell-Rechner laufen weiterhin völlig unverändert. In den gemeinsamen configuration.nix und home.nix wurde kein einziger Buchstabe angefasst. Alles was der Lenovo anders macht, steht in seinen eigenen zwei Dateien.
Dazu, cool wie immer: Änderungen pflege ich für meine ganze Flotte an dem Rechner, an dem ich zufällig gerade sitze - die flake.nix (bzw. flake.lock) im Zusammenspiel mit Codeberg verteilt es dann an alle anderen. Und Systempflege weiterhin mit nur einem einzigen Befehl.
Ich finde gerade heute zeigte sich NixOS mal wieder von seiner besten Seite. Es war zum Abschluss nochmal ein schönes Beispiel für das Potenzial des Paradigmenwechsels durch den konsequent und logisch durchdachten deklarativen Ansatz.
Abgesehen vom geringen Aufwand für die Systempflege gefällt mir nach den Monaten mit NixOS am besten, dass ich erstmals das Gefühl habe wirklich Herr meines Systems zu sein. Nichts versteckt sich hinter Eingabemasken oder Linux-Befehlen welche ich nicht genau entschlüsseln kann. Bei NixOS weiß ich ganz genau, an welcher Stelle in den wenigen Konfigurationsdatein welche Systemeinstellung beschrieben ist - in normalen Textdateien und in einer einheitlichen Sprache, welche über die Dokumentation (zentral, an einer Stelle - siehe Teil 2) gelernt und entschlüsselt werden kann.
So, das war's von meiner Seite. Aber das muss nicht alles gewesen sein. Wenn du spannende und hilfreiche Aspekte oder News zu NixOS weitergeben möchtest, dann setz dich gerne an die Tastatur und reiche deinen Artikelvorschlag direkt hier auf der Page unter "Mitschreiben" ein.
Ich würde mich sehr über konstruktive Kritik zu der NixOS-Reihe in den Kommentaren freuen! Gebt dort gerne auch eure eigenen Erfahrungen mit NixOS weiter.
Tschüss einstweilen!
Artikel der NixOS-Reihe
GNU/Linux.ch ist ein Community-Projekt. Bei uns kannst du nicht nur mitlesen, sondern auch selbst aktiv werden. Wir freuen uns, wenn du mit uns über die Artikel in unseren
Chat-Gruppen oder im
Fediverse diskutierst. Auch du selbst kannst Autor werden. Reiche uns deinen Artikelvorschlag über das
Formular auf unserer Webseite ein.