Perchè utilizzo NixOS

Sommario

Inauguriamo il primo post di questo blog parlando un po della mia distro preferita, NixOS, che utilizzo da ormai quasi un anno e che non ho intenzione di cambiare a breve. Ma perchè mi piace così tanto NixOS? Per parlarne in modo più approfondito è necessario un po di contesto e capire da dove arrivo.

Esperienze passate

Da quando utilizzo GNU/Linux, io sono sempre stato fan accanito di arch linux per la sua semplicità, modularità, documentazione e per la grande repository di pacchetti che mi permetteva così di partire con un sistema “vuoto” e personalizzarlo come volevo io. Ma c’è sempre stato un problema che mi impediva di esprimere la mia creatività, ovvero la fragilità del sistema operativo.

Ogni tanto mi ritrovavo con un sistema inutilizzabile, dato da errori durante la compilazione di pacchetti presi dall’aur, da un errore umano commesso durante la scrittura di un file config o semplicemente dopo aver aggiornato i pacchetti. Questo per me è sempre stato un problema che davo per scontanto non risolvibile perchè non c’era un effettiva soluzione che potesse prevenire le rotture del sistema e quindi mi sentivo come se stessi camminando sui gusci d’uovo, dovendo far attenzione ogni volta che volevo modificare il sistema o aggiornarlo.

Come conseguenza spuntano due ulteriori problemi:

  • aggiustare il sistema operativo: Di base non mi dispiace fare troubleshooting ma quando sono obbligato a farlo, specialmente quando erano pianificati altri impegni, si crea un disagio notevole.
  • possibile perdita dello stato del sistema: Quando mi ritrovavo in situazioni catastrofiche, andavo a re-installare il sistema operativo e, anche se avevo i backup pronti con i file di configurazione, dovevo comunque installare tutti i pacchetti, riconfigurare i servizi systemd… insomma perdevo un sacco di tempo e non era garantito che sarei ritornato al punto di prima.

Il cambio a NixOS

fastfetch screenshot

fastfetch screenshot

Quando ho saputo che esisteva un sistema operativo che potesse risolvere definitivamente i problemi citati sopra, non c’ho pensato due volte e l’ho installato e… ho capito perchè non è così popolare.

Si creano due nuovi problemi:

  • la curva di apprendimento;
  • la documentazione;

Questi problemi non sono intrinsechi all’architettura del sistema operativo, ma bensì limitano solo l’esperienza di configurazione iniziale e intermedia dell’utente. Mi piace pensare che questo sia la “soglia” da varcare per poter ricevere la “ricompensa”, come se fosse un investimento a lungo termine.

Perchè all’inizio si perderà parecchio (no veramente, parecchio tempo) cercando di capire il funzionamento, il paradigma e in generale cambiare l’approccio al sistema operativo. Il motivo per cui è così difficile all’inizio prendere mano con le meccaniche di NixOS è dato dal suo NON essere FHS-compliant1 e dal cambio di paradigma, passando da imperativo (distro tradizionale) a dichiarativo (NixOS). Infatti al posto di utilizzare comandi come systemctl --user enable waybar, si va a definire lo stato del sistema attraverso un file di configurazione, proprio come i dotfiles per intenderci.

Confronto: Arch Linux vs NixOS

Caratteristica Arch Linux (Tradizionale) NixOS (Dichiarativo)
Metodo di Configurazione Imperativo (comandi sequenziali) Dichiarativo (stato definito in file)
Riproducibilità Bassa (dipende dall’ordine delle operazioni) Alta (lo stesso file config genera lo stesso sistema)
Gestione Errori Manuale (debugging del sistema rotto) Automatico (rollback a generazioni precedenti)
Attenzione Richiesta Alta (rischio di rompere il sistema) Media (curva di apprendimento iniziale)
Flessibilità Modulare ma fragile Puro e isolato

esempio modulo per il servizio waybar

{ vars, ... }:
let
	script_folder = vars.root + "/assets/scripts";
	config = (builtins.fromJSON (builtins.readFile ./config.jsonc));
	style = builtins.readFile ./style.css;
	iconPath = vars.root + "/assets/imgs/icons";
	nix_logo = "${iconPath}/NixOS.png";

	modules = with customs;
	{"custom/muted" = muted;} //
	{"custom/notification" = notification;} //
	{"custom/llama" = load_llama;} //
	{"custom/speaker" = speaker;} //
	{"image#nix_logo" = logo;};
in
{
	programs.waybar = {
		enable = true;
		settings = [(config // modules)];
		style = builtins.toString style;
		systemd.enable = true;
		systemd.enableDebug = false;
		systemd.target = "graphical-session.target";
	};
}

Stress-free distro

Una volta affrontato i primi problemi e creato un file di configurazione “decente” che potesse permettermi di avere un sistema operativo usabile, ho iniziato a notare istantaneamente la “libertà di movimento” nelle mie mani. Infatti il primo vantaggio che ho notato era la libertà di sperimentare, non solo attraverso ambienti di sviluppo2, ma anche globalmente e in caso di errori (anche catastrofici come un sistema che non risponde) basta ritornare ad una generazione vecchia utilizzando il comando nixos-rebuild switch dopo aver cambiato la config (in questi casi io utilizzo il checkout di git) o, in caso di un sistema rotto, attraverso la boot entry di grub. Infatti NixOS espone automaticamente le ultime N generazioni come possibili boot entries nel bootloader.

La cosa più bella e magica è che ritorni esattamente al punto in cui hai “salvato” quasi come se fosse un checkpoint di un videogioco, a patto che tu abbia definito dichiarativamente ogni aspetto del sistema operativo. Ma ciò non è sempre facile o scontato, perchè inevitabilmente ci si ritrova in pacchetti mancanti o configurazioni per un servizio non esistenti.

Derivazioni

Qui inizia la seconda parte difficile di NixOS perchè, se appunto ti manca un determinato servizio o programma e vuoi continuare con un sistema “puro” (secondo la filosofia di Nix), allora sei “costretto” a creare le derivazioni. Generalizzando, le derivations sono delle espressioni Nix che descrivono come installare un determinato programma, servizio, etc…

Nella mia esperienza, poche volte mi son ritrovato in situazioni in cui avrei potuto utilizzare le derivations ma non ho trovato necessario farlo perchè il pacchetto l’avrei usato raramente o mi sarebbe servito solo per un progetto isolato. Quindi mi son limitato a creare degli ambienti di sviluppo e buildare l’eseguibile.

Perchè non utilizzo un sistema puro

Non è necessario creare una derivazione per poter eseguire applicazioni, ma in un certo senso questo va contro la filosofia di Nix. Andare a dover buildare manualmente pacchetti è un processo imperativo che, in caso di wipe completo, andrà ripetuto.

Mi sta bene fare in questo modo perchè è un buon compromesso tra facilità e portabilità. In più dato che utilizzo un sistema di versionaggio per i pacchetti che vado a buildare manualmente, i passaggi che dovrò rifare saranno minori, specialmente se ci incorporo anche un makefile. Infatti mi basterà fare un git clone e make.

Posso capire la soddisfazione che un sistema puro al 100% offre ma credo che sia estremamente difficile farlo e, nel mio caso, non ne vale molto la pena. Bisognerebbe aggiungere overlays3 o derivations anche per la più piccola flag di un programma che magari viene usato poco, e andrei a perdere più tempo a configurare che a modificare manualmente. Chiaramente devo tener traccia di ogni configurazione importante inserita imperativamente, altrimenti si rischiano comportamenti non deterministici una volta che si re-installa da zero il sistema.

Conclusione

Utilizzare NixOS in un certo senso mi ha permesso di “non utilizzarlo”, questo perchè ora posso dare più importanza al lavoro e alle passioni che svolgo sul computer al posto di essere schiavo del proprio sistema operativo. Ovviamente ciò ha il costo della fatica iniziale ma alla fine è come scalare una montagna: Una volta raggiunto la cima, il panorama è mozzafiato.


  1. Filesystem Hierarchy Standard↩︎

  2. development shell; Ambienti di sviluppo dove è possibile creare una config nix per un determinato progetto senza intaccare la configurazione principale del sistema. ↩︎

  3. “Modifiche” ad una derivazione già esistente ↩︎