Lässt sich ein kleineres NixOS-ISO bauen?
(natkr.com)- NixOS macht es leicht, allein per Konfiguration eine VM oder ein ISO zu bauen, aber selbst ein nahezu minimales Live-Image wurde von Beginn an mit 458 MiB erzeugt und lag damit deutlich über dem Alpine-VM-ISO mit etwa 66 MiB
- Den Großteil der Größe belegte nix-store.squashfs; darin steckten Python 3.13.13, Linux-Module, systemd, Perl, GRUB, Dokumentation und Nix-bezogene Abhängigkeiten
- Mit
nix.enable = false,documentation.enable = falseund dem Entfernen vonregister-nix-pathsschrumpfte das ISO von 458 MiB → 384 MiB → 360 MiB, und auch die Boost-Abhängigkeit verschwand - Nach dem Entfernen des OpenSSH-Clients, der Standardpakete, der GRUB-Installationswerkzeuge, der Kernel-Module zur Laufzeit und des Perl-basierten Aktivierungspfads fiel die endgültige Größe auf 183 MiB
- Für kleine experimentelle Boot-Images ist das nützlich, aber da dabei viele benötigte Funktionen entfernt werden, eignet es sich kaum unverändert für Desktops oder wichtige Umgebungen
ISO aus einer NixOS-Konfiguration bauen
- NixOS kann auf Basis einer Konfiguration leicht eine VM erzeugen
nixos-rebuild build-vmerstellt eine VM aus der aktuellen Systemkonfiguration- Mit
pkgs.nixoslässt sich auch aus einer beliebigen Konfiguration eine VM erzeugen, selbst wenn sie nicht die Systemkonfiguration ist
- Das Grundbeispiel erzeugt eine minimale VM, die nur
system.stateVersion = "26.05"undservices.getty.autologinUser = "root"setzt - Diese VM arbeitet als thin VM
- Im Disk-Image liegen nur Dateien, die direkt innerhalb der VM erstellt wurden
/nix/storeund der Rest werden vom Host-OS eingehängt
- Wenn auf dem Host kein Nix vorhanden ist oder das System auf einem entfernten Host bzw. einem normalen Hypervisor laufen soll, braucht man ein eigenständiges ISO
- Importiert man in NixOS das Modul
iso-image.nix, kann man ein ISO bauen- Mit
image.baseName = lib.mkForce "nixos"wird der Name des erzeugten ISO festgelegt - Ein Beispielaufruf sieht so aus:
qemu-system-x86_64 --cdrom .../nixos.iso -m 1G --accel kvm - Außerhalb einer modernen Linux-Umgebung auf amd64 müssen möglicherweise Architektur oder Beschleunigungsmethode angepasst werden
- Mit
Ausgangspunkt: 458-MiB-ISO
- Das Ergebnis des Standard-ISO-Builds lag bei 458 MiB
- Dieses Image enthielt noch nicht einmal
vim- Nach dem Booten ergab
vimnurcommand not found
- Nach dem Booten ergab
- Als Vergleich diente ein Alpine-VM-ISO mit etwa 66 MiB
- Damn Small Linux wird als Beispiel erwähnt, das bei viel kleinerer Größe trotzdem eine brauchbare Desktop-Umgebung bot
- Das Ziel war nicht, das Niveau von Damn Small Linux zu erreichen, sondern zu prüfen, ob sich ein NixOS-ISO überhaupt spürbar verkleinern lässt
Größenanalyse des ISO-Inhalts
- Nach dem Mounten des ISO zeigte
dufolgende Aufteilungnix-store.squashfs: 416 MiB- initrd: 26 MiB
- Kernel: 13 MiB
- gesamtes ISO: 458 MiB
- Der entscheidende Faktor war also nix-store.squashfs, also der eigentliche Userspace
- Nach dem Mounten des squashfs fanden sich darin Pfade wie in einem Nix-Store
python3-3.13.13: 128 MiBlinux-6.18.35-modules: 144 MiBsystemd-260.1: 60 MiBperl-5.42.0: 56 MiBgrub-2.12: zusammen über mehrere Einträge etwa 62 MiB- Auch Dokumentation wie
nix-manual-2.34.7undnixos-manual-htmlwar enthalten
- Da das ISO auf dem Host gebaut wird, lassen sich die Store-Pfade aus dem ISO auch im Host-
/nix/storenachvollziehen - Mit
nix why-dependswurde die Herkunft der Abhängigkeiten untersucht- Boost kam über den Pfad des Nix-Daemons hinein
- Über
nix-daemon.conf,nixundlibnixutil.soführte die Kette bis zuboost-1.89.0
Nix und Dokumentation entfernen
- Mit
nix.enable = falsewurde versucht, Nix selbst aus dem Image zu entfernen - Mit
documentation.enable = falsewurde auch die Dokumentation deaktiviert - Das erste Ergebnis war eine Schrumpfung von 458 MiB → 384 MiB
- Boost war jedoch weiterhin vorhanden
register-nix-paths.servicewollte beim Booten den Inhalt des ISO-Stores registrieren- Dieser Pfad zog erneut Nix und Boost herein
- Mit
systemd.services.register-nix-paths = lib.mkForce {}wurde dieser Dienst geleert und entfernt - Danach war das ISO 360 MiB groß, und
nix why-dependszeigte keine Boost-Abhängigkeit mehr
OpenSSH und Standardpakete entfernen
- Auf ähnliche Weise ließ sich auch
environment.defaultPackagesleeren - Das Entfernen von
sshwar kompliziertermodules/programs/ssh.nixfügt OpenSSH zuenvironment.corePackageshinzu- Eine Option wie
programs.ssh.enablezur Steuerung ließ sich nicht finden services.openssh.enableist eine Servereinstellung und keine Option zum Entfernen des Clients
- Zwar ließ sich
programs/ssh.nixmitdisabledModulesausschließen, aber andere Module erwarteten weiterhin das Vorhandensein vonprograms.ssh, was zu Folgefehlern führte - Die Lösung war ein separates Modul mit einer Stub-Option, das zwar
programs.sshbereitstellt, aber nicht verwendetoptions.programs.ssh = lib.mkOption {};- Danach wurde das eigentliche SSH-Modul mit
disabledModules = [ "programs/ssh.nix" ];ausgeschlossen
- In diesem Schritt wurden außerdem folgende Einstellungen gesetzt
documentation.man.enable = falsenetworking.firewall.enable = falseenvironment.defaultPackages = lib.mkForce []
Notiz zur Struktur von NixOS-Modulen
- NixOS-Module bestehen grob aus drei Teilen
- Einträgen auf Modulebene:
imports,disabledModules - Optionsdefinitionen:
options.* - Implementierung:
config.*
- Einträgen auf Modulebene:
- Module, die keine Optionen definieren, können eine Kurzform verwenden und Implementierungseigenschaften ohne
config.-Präfix schreiben - Um diese Kurzform im übrigen Setup beizubehalten, wurde die Stub-Option
programs.sshin ein separates importiertes Modul ausgelagert
GRUB-Installationswerkzeuge entfernen
- Einer der verbliebenen großen Posten waren GRUB-bezogene Dateien mit etwa 62 MiB
- Der Bootloader selbst wird benötigt, aber es erschien unnötig, sämtliche Installationswerkzeuge mitzuliefern
- Das NixOS-ISO-Preset bündelt sowohl UEFI- als auch BIOS-Versionen von GRUB
- Da es dafür keine klare Abschaltoption gab, wurden auf gröbere Weise folgende Werte zurückgesetzt
system.extraDependencies = lib.mkForce []environment.systemPackages = lib.mkForce config.environment.corePackages
environment.systemPackageswurde nicht vollständig geleert- Ohne
bashkanngettyin eine Crash-Schleife geraten, daher wurden diecorePackagesbeibehalten, damit die Shell zumindest grundlegend funktioniert
- Ohne
Kernel-Module entfernen
linux-6.18.35-moduleswar 144 MiB groß und machte etwa ein Viertel der Gesamtgröße aus- In NixOS schien es keinen guten Hook zu geben, um die zur Laufzeit nutzbaren Kernel-Module zu begrenzen
- Stattdessen wurde im System-Output der Ordner
kernel-modulesentferntsystem.systemBuilderCommands = lib.mkAfter "rm $out/kernel-modules";
- Dadurch wird das Laden von Laufzeitmodulen praktisch deaktiviert
- Benötigte Module müssen in
boot.initrd.kernelModulesoderavailableKernelModulesaufgenommen werden
- Benötigte Module müssen in
- Nach dieser Änderung funktionierte das Booten weiterhin, aber die Möglichkeit, auf eine angenehmere Bildschirmauflösung umzuschalten, ging verloren
- Die ISO-Größe sank auf 197 MiB
Perl entfernen und experimentelle Ersatzfunktionen
- Es war weiterhin Perl mit 56 MiB enthalten
- Mit
nix why-dependszeigte sich, dass Perl bei der Systemaktivierung für Benutzer und/etcverwendet wurde - Auf die Einrichtung von Benutzern und
/etckonnte nicht vollständig verzichtet werden - Stattdessen wurden diese Pfade durch experimentelle Funktionen ersetzt
- Für die Verwaltung von
/etcwurde ein Overlay-Ansatz verwendet - Für die Benutzerverwaltung kam das native userborn zum Einsatz
- Für die Verwaltung von
- Verwendet wurden folgende Einstellungen
system.etc.overlay.enable = truesystem.etc.overlay.mutable = falseservices.userborn.enable = true
- Die endgültige ISO-Größe betrug 183 MiB
Endstand und Grenzen
- Vom Startwert 458 MiB sank das ISO auf 183 MiB, also auf fast ein Drittel der Ausgangsgröße
- Trotzdem wird das Ergebnis nicht als wirklich „gut“ bewertet
- Für Desktops oder wichtige produktive Umgebungen ist es nicht geeignet
- Alle entfernten Funktionen haben einen Grund, überhaupt vorhanden zu sein
- Wenn man ein kleines experimentelles Boot-Image braucht und nur sehr kleine Aufgaben ausführen will, kann das als Anhaltspunkt dienen
- Wer die finale Konfiguration unverändert übernimmt, dem könnten genau die Funktionen fehlen, die für den eigenen Zweck nötig sind
Weiteres Potenzial zur Verkleinerung
- Diese Arbeit konzentrierte sich auf Teile, die sich „einfach entfernen“ ließen oder für die es relativ klare Ersatzwege gab
- Bereiche mit tieferem Eingriff bleiben weiterhin offen
- Derzeit werden sowohl
systemdMinimalals auchsystemdmitgebündelt - Der Versuch, eines von beiden zu entfernen, ließ andere Build-Pfade scheitern
- Derzeit werden sowohl
- Auch kleinere Posten ließen sich weiter abbauen, und in Summe könnte das noch relevant werden
- Für weitere Optimierungen wären zusätzliche Untersuchung und Experimente nötig
1 Kommentare
Lobste.rs-Meinungen
Dafür gibt es ein Modul, das genau für diesen Zweck gemacht wurde. Es erfordert zwar einiges an Kompilierung, kann aber ein vollständig eigenständiges initrd inklusive des gesamten NixOS-Userlands mit zstd-Komprimierung auf etwa 80 MiB bringen
Das ist nicht nur auf eigenständige initrds beschränkt, sondern kann genutzt werden, um jedes NixOS zu verkleinern. Vermutlich ließe sich das auch auf eine Installations-ISO anwenden
https://github.com/wucke13/minimal-nixos/
Das Basissystem von TinyCore Linux ist Core mit 17 MB
Wenn man X und FLTK/FLWM will, gibt es TinyCore mit 23 MB, und wenn man noch mehr Fenstermanager und Apps möchte, gibt es CorePlus mit 248 MB
http://www.tinycorelinux.net/downloads.html
Ich empfehle den Vortrag auf der NixCon, in dem NixOS als Yocto-Alternative stark verkleinert wurde: https://youtu.be/AsXY61laNb8
Er war nicht so detailliert, wie ich gehofft hatte, aber das, was ich auf der Konferenz direkt von Óli und Matthew gehört habe, war großartig. Ich frage mich, ob es dazu irgendwo eine Zusammenfassung gibt
Es ist bei NixOS immer etwas frustrierend, kleine Installations-Setups zu bauen
Die Größe rund um SSH lässt sich mit den folgenden Einstellungen wohl reduzieren
Man kann auch
"${nixpkgs}/nixos/modules/profiles/minimal.nix"importieren. Dort sind einige der im Artikel genannten Optimierungen bereits enthaltenTrotzdem ist dieser Ansatz in den meisten Fällen wahrscheinlich sinnvoller
"${nixpkgs}/nixos/modules/profiles/minimal.nix"hatte ich mir früher schon einmal angesehen und fand es weniger hilfreich als erwartet, daher kam mir beim Start meiner Untersuchung nicht in den Sinn, es einzubeziehen. Als ich später wieder daran dachte, war ich schon etwa halb fertig, und es noch nachträglich an den Anfang zu setzen, wo es eigentlich hingehört hätte, fühlte sich etwas unehrlich anEs ist seltsam, wie oft Perl sich in Systeme hineinzieht. Selbst bei kleinen ISOs ist Perl dabei, und wenn man von Grund auf etwas Ordentliches kompilieren will, landet man schnell bei openssl -> Perl
Noch bevor ich es gelesen habe, habe ich vermutet, dass wieder irgendein dummes Perl-Skript, das niemand in C neu geschrieben hat, daran schuld ist
Korrektur: Genau so war es
NixOS verwendet ab 26.05 im Standard-initrd systemd, weil es viele initrd-Anwendungsfälle gibt, die moderne Betriebssysteme unterstützen müssen
systemdMinimalist ein systemd-Binary, das mit weniger Flags und Abhängigkeiten kompiliert wurde, was hilft, das initrd kleiner zu haltenWenn das Ziel allerdings eine minimale ISO ist, könnte man wohl auch beide vom selben Binary abhängig machen