NixOS con meno di 100 MB di RAM: come costruire un server Linux ultraleggero

Un esperimento porta NixOS a 96 MB di RAM e permette aggiornamenti su VPS da 512 MB. Analizziamo kernel, huge page, zram, garbage collector e servizi da ottimizzare, con configurazioni pratiche e limiti da conoscere.
NixOS con meno di 100 MB di RAM: come costruire un server Linux ultraleggero

Un server Linux capace di funzionare con appena 96 MB di RAM, senza rinunciare a systemd, SSH, networking e alla possibilità di aggiornare il sistema operativo. Non si tratta di una distribuzione minimale costruita appositamente, ma di NixOS 26.05, configurato per ridurre drasticamente il consumo di memoria su un VPS (Virtual Private Server) equipaggiato con appena 512 MB di memoria RAM.

Il risultato è interessante soprattutto perché riguarda una distribuzione Linux dichiarativa, capace di ricostruire l’intera configurazione del sistema attraverso un unico comando. NixOS è infatti ricompresa tra le distro Linux immutabili anche se il suo modello si distingue da quello adottato da molte altre soluzioni: la configurazione del sistema non dipende soltanto dallo stato corrente dei file presenti sul disco, ma è descritta in modo esplicito attraverso file dichiarativi.

Cosa significa che NixOS è una distribuzione Linux dichiarativa

Nel caso di NixOS, amministratore e sistema operativo condividono una sorta di “ricetta” che specifica pacchetti, servizi, utenti, parametri del kernel, configurazioni di rete e molte altre impostazioni.

Quando si esegue un comando come nixos-rebuild switch, NixOS legge tale configurazione, costruisce una nuova generazione del sistema e la rende attiva senza modificare in modo incontrollato quella precedente.

È proprio questo meccanismo, però, a rendere particolarmente interessante il caso di un VPS con appena 512 MB di RAM. La fase di rebuild non si limita infatti a copiare qualche file di configurazione: implica una valutazione delle espressioni Nix (fase in cui Nix legge e interpreta i file di configurazione, risolve le dipendenze e determina quali pacchetti e componenti servono per costruire il sistema, prima di scaricarli o compilarli), risoluzione delle dipendenze, creazione di derivazioni, decompressione di pacchetti e, in alcuni casi, compilazione locale. Tutte operazioni che generano picchi di memoria sensibilmente superiori a quelli osservabili quando il server è semplicemente acceso e inattivo.

Ridurre NixOS sotto la soglia dei 100 MB in idle, quindi, presuppone una serie di interventi su più livelli: kernel, moduli caricati, gestione della memoria compressa, huge page (pagina di memoria RAM di dimensioni maggiori rispetto ai tradizionali 4 KB, utilizzata per rendere più efficiente la gestione della memoria), servizi di sistema e comportamento del garbage collector (meccanismo che individua e libera automaticamente la memoria occupata da dati non più utilizzati). Il risultato finale è un sistema che conserva i vantaggi dell’approccio dichiarativo di NixOS, ma con un’impronta RAM talmente ridotta da renderlo utilizzabile anche su VPS estremamente economici.

Ottimizzazioni RAM NixOS server Linux VPS

Com’è nato NixOS e l’esperimento che lo ha reso superleggero

NixOS nasce nel 2003 come progetto di ricerca legato al gestore di pacchetti Nix e si distingue dalle distribuzioni tradizionali per l’uso di configurazioni riproducibili, generazioni di sistema e rollback. Sono caratteristiche preziose per amministrare server, ma che comportano un certo consumo di risorse, soprattutto durante la valutazione delle configurazioni e gli aggiornamenti.

Un esperimento pubblicato su LowEndSpirit da Anthony Smith ci ha davvero colpito e dimostra quanto si possa guadagnare intervenendo sulle componenti giuste. La configurazione iniziale utilizzava circa 288 MB di RAM, caricava 89 moduli del kernel e richiedeva un picco di 896 MB per una ricostruzione ordinaria del sistema. Dopo 3 giorni di ottimizzazioni, il consumo a riposo è sceso a 96 MB, con 54 moduli caricati.

Dato ancora più significativo: un aggiornamento completo con ricostruzione ha raggiunto un picco di 371 MB, senza esaurire i 512 MB disponibili sulla macchina.

Prima di ottimizzare: misurare quanta RAM utilizza davvero Linux

Il lavoro condotto da Anthony Smith è interessante non soltanto per il risultato raggiunto, ma soprattutto per il metodo utilizzato.

Per ottenere un server Linux con un consumo minimo di RAM si potrebbe scegliere direttamente una distribuzione progettata per essere particolarmente leggera, come Alpine Linux. Partire da NixOS, invece, significa affrontare un problema più complesso e, proprio per questo, decisamente più istruttivo.

L’esperimento permette di comprendere come Linux utilizza realmente la memoria, quanto incidono i moduli del kernel, quali risorse impegnano i servizi di sistema e perché alcune operazioni, apparentemente innocue, possono provocare picchi di consumo molto elevati. Non si tratta soltanto di eliminare componenti superflui: bisogna capire quali siano indispensabili, come interagiscano tra loro e quali conseguenze comporti la loro disattivazione.

È un esercizio particolarmente utile per chi vuole approfondire l’amministrazione Linux. Le conoscenze acquisite non riguardano soltanto NixOS, ma possono essere applicate anche a Debian, Ubuntu e ad altre distribuzioni, soprattutto quando si amministrano VPS economici, sistemi embedded o macchine con risorse limitate.

Il primo passo, quindi, non consiste nel modificare la configurazione, ma nel misurare il consumo di memoria e individuarne le cause.

I controlli sul VPS con NixOS installato

Su un VPS NixOS conviene cominciare con alcuni controlli, preferibilmente dopo un riavvio e senza sessioni amministrative aperte.

I comandi free -m, cat /proc/meminfo e lsmod | wc -l permettono di osservare rispettivamente l’utilizzo della memoria, le statistiche del kernel e il numero dei moduli caricati.

Per capire quali componenti consumano più memoria, si può utilizzare il comando systemd-cgtop, che mostra le risorse impiegate dai servizi e dai gruppi di processi gestiti da systemd. Con systemctl --type=service --state=running si ottiene invece l’elenco dei servizi attualmente in esecuzione: un buon punto di partenza per individuare quelli che potrebbero essere disattivati.

Per un’analisi più precisa torna utile smem, uno strumento che permette di misurare la memoria effettivamente occupata dai singoli processi. In particolare, il valore PSS (Proportional Set Size) ripartisce tra i processi la memoria che condividono, ad esempio quella utilizzata dalle librerie comuni. Si evita così di conteggiare più volte le stesse aree di RAM.

La RAM occupata, comunque, non è per forza memoria sprecata: Linux utilizza la memoria disponibile anche per conservare temporaneamente file e dati già letti dal disco, accelerando le operazioni successive. Quando un’applicazione richiede più RAM, il kernel può liberare buona parte di questa cache.

Per sapere quanta memoria sia realmente disponibile, è preferibile controllare il valore MemAvailable, visibile con free -m oppure in /proc/meminfo. A differenza della semplice memoria libera, tiene conto anche delle risorse che Linux può recuperare senza ricorrere in modo significativo allo swap.

L’obiettivo delle misurazioni è distinguere la memoria occupata dai servizi e dal kernel da quella utilizzata come cache, evitando di eliminare componenti utili soltanto per ottenere numeri apparentemente migliori.

Alleggerire il kernel Linux: quali componenti eliminare

Una parte consistente del lavoro svolto da Smith riguarda il kernel: un VPS KVM dispone di hardware virtualizzato relativamente prevedibile. In generale, non richiede driver per schede audio, Bluetooth, periferiche USB fisiche, controller WiFi o numerose altre interfacce presenti nei computer tradizionali.

La prima verifica consiste nell’esaminare i moduli caricati con lsmod e identificare quelli superflui. In NixOS si possono dichiarare esclusioni nel file /etc/nixos/configuration.nix mediante boot.blacklistedKernelModules.

Per esempio, su una macchina virtuale che non utilizza periferiche corrispondenti, si può valutare questa configurazione:

boot.blacklistedKernelModules = [ "floppy" "psmouse" "joydev" "cfg80211" ];

L’autore dell’esperimento ha ottenuto un ulteriore risparmio riducendo l’insieme dei moduli presenti sul disco, attraverso una derivazione Nix che modifica system.modulesTree e conserva le dipendenze dei moduli mantenuti. La dimensione della relativa directory è passata da 162 a 45 MB. Si tratta principalmente di un risparmio di spazio su disco, non di 117 MB di RAM recuperati direttamente. La rimozione impedisce però anche il caricamento automatico di driver non più disponibili.

Un’altra modifica riguarda l’initrd, l’ambiente temporaneo utilizzato prima di montare il filesystem principale. Con boot.initrd.includeDefaultModules = false; è possibile evitare l’inclusione dei moduli predefiniti; occorre però dichiarare esplicitamente quelli indispensabili. Nella macchina dell’esperimento bastavano virtio_pci, virtio_blk ed ext4. Su VPS con unità NVMe virtuali, VirtIO SCSI, cifratura o LVM, la configurazione ovviamente può cambiare.

Transparent Huge Pages: perché disattivarle può recuperare memoria

Una delle ottimizzazioni più efficaci ha riguardato le Transparent Huge Pages (THP), una funzionalità del kernel che permette di utilizzare pagine di memoria di dimensioni maggiori rispetto alle tradizionali pagine da 4 KB. Sui sistemi x86-64, una dimensione molto comune per le huge page è 2 MB.

Le THP possono migliorare le prestazioni riducendo il numero delle traduzioni degli indirizzi virtuali e la pressione sulla TLB del processore. Tuttavia, comportano anche costi legati alla gestione, alla compattazione e alle riserve di memoria.

Nell’esperimento, disattivandole mediante il parametro transparent_hugepage=never, l’autore ha recuperato 52 MB su una VM da 2 GB e ottenuto 103 MB aggiuntivi di memoria disponibile su una configurazione da 896 MB. Il beneficio dipendeva anche dal comportamento osservato di vm.min_free_kbytes, che con THP attive raggiungeva valori particolarmente elevati.

Per riprodurre la modifica su NixOS si può aggiungere:

boot.kernelParams = [ "transparent_hugepage=never" ];

Dopo il riavvio, il comando cat /sys/kernel/mm/transparent_hugepage/enabled consente di verificare l’impostazione. La documentazione del kernel Linux chiarisce comunque che alcune allocazioni esplicite possono ancora utilizzare huge page.

A margine, diciamo però che la disattivazione di THP non è e non può essere una scelta universalmente vantaggiosa: database, applicazioni con grandi aree di memoria e carichi particolarmente sensibili alle prestazioni della TLB potrebbero risentirne.

zram e zstd: come aggiornare NixOS con appena 512 MB

Il vero salto di qualità durante la ricostruzione del sistema arriva dall’utilizzo di zram: il modulo del kernel crea un dispositivo a blocchi nella RAM, sul quale i dati vengono compressi prima della memorizzazione. Configurandolo come swap, Linux può trasferirvi pagine di memoria poco utilizzate anziché conservarle interamente nella loro forma originale.

A differenza dello swap tradizionale su disco, zram non richiede operazioni di lettura e scrittura verso un SSD o un supporto esterno. La compressione comporta un consumo di CPU e una certa quantità di memoria per i metadati, ma può aumentare sensibilmente la quantità di dati residenti nella RAM fisica.

NixOS mette a disposizione il modulo zramSwap; una configurazione utile per iniziare è la seguente:

zramSwap.enable = true;

zramSwap.algorithm = "zstd";

zramSwap.memoryPercent = 100;

zramSwap.memoryMax = 536870912;

Con queste impostazioni, il dispositivo di swap può contenere fino a 512 MB di dati non compressi, nel caso di un VPS con 512 MB di memoria. Non significa che il kernel riservi immediatamente altri 512 MB fisici: la memoria effettivamente utilizzata dipende dalla quantità di dati trasferiti e dal rapporto di compressione.

L’autore ha scelto zstd per il rapporto fra velocità e compressione, arrivando a osservare un consumo massimo di circa 116 MB di RAM fisica per zram durante la ricostruzione. La documentazione ufficiale di zram descrive le statistiche disponibili in sysfs; i comandi zramctl e swapon --show permettono di controllare il dispositivo durante l’utilizzo.

Il garbage collector di Nix: ridurre i picchi durante la valutazione

Un’altra parte importante dell’ottimizzazione riguarda il Boehm Garbage Collector, utilizzato dal valutatore Nix per recuperare memoria occupata da oggetti non più raggiungibili.

Il parametro GC_INITIAL_HEAP_SIZE controlla la dimensione iniziale dell’heap ovvero la quantità di memoria inizialmente destinata a contenere i dati che un programma alloca dinamicamente durante l’esecuzione. Impostando il parametro a 33554432 byte, equivalenti a 32 MB, si riduce la dimensione iniziale richiesta al garbage collector. GC_FREE_SPACE_DIVISOR=8 ne modifica la politica di espansione, mentre GC_FORCE_UNMAP_ON_GCOLLECT=1 favorisce la restituzione al sistema operativo delle pagine liberate.

Come applicare le modifiche senza rendere inutilizzabile il VPS

Su una macchina remota conviene procedere per gruppi di modifiche, mantenendo inizialmente invariati i componenti necessari al boot, alla rete e all’accesso SSH. Si può partire da zram, misurare il consumo, intervenire sulle THP e infine valutare la rimozione dei servizi meno utili.

Prima di ogni riavvio è opportuno verificare la configurazione con sudo nixos-rebuild build e, quando appropriato, provarla con sudo nixos-rebuild test. Il comando test attiva la nuova configurazione senza renderla la generazione predefinita al boot; non garantisce comunque di poter recuperare una macchina che perde l’accesso di rete.

Per le modifiche ai moduli essenziali dell’initrd bisogna ricorrere all’uso di una console di emergenza offerta dal provider. È prudente conservare una configurazione funzionante e verificare la possibilità di rollback prima di eliminare componenti.

Ma vale davvero la pena spingersi fino a questo livello di personalizzazione?

Su un server con diversi gigabyte di RAM, risparmiare qualche decina di megabyte difficilmente giustifica il tempo necessario per analizzare ogni modulo, modificare i parametri del kernel e verificare la compatibilità delle impostazioni dopo gli aggiornamenti. Una configurazione troppo aggressiva rischia anzi di complicare la manutenzione, compromettere alcune funzionalità e rendere più difficile individuare l’origine di eventuali problemi.

Il discorso cambia quando si amministrano VPS con risorse estremamente limitate, si devono mantenere attive molte istanze virtuali oppure si lavora su dispositivi embedded. In simili situazioni, recuperare anche soltanto 50 o 100 MB può consentire di eseguire un servizio aggiuntivo, evitare il ricorso continuo allo swap o completare operazioni che altrimenti terminerebbero per memoria insufficiente.

Va considerato anche un altro aspetto: un sistema più leggero non è automaticamente più veloce, sicuro o affidabile. La disattivazione di un servizio può ridurre la superficie di attacco, ma eliminare un componente dedicato alla gestione della memoria, alla sincronizzazione dell’orologio o alla risoluzione DNS può introdurre problemi difficili da riconoscere immediatamente. Allo stesso modo, una configurazione del kernel ottimale per un VPS KVM potrebbe risultare inadatta a un’altra piattaforma di virtualizzazione.

Il valore dell’esperimento di Anthony Smith sta nel dimostrare quanto si possa imparare analizzando ciò che Linux carica, conserva in memoria e utilizza durante le operazioni più impegnative. La capacità di distinguere un consumo realmente necessario da uno evitabile è una competenza che torna utile ben oltre NixOS.

Ti consigliamo anche

Link copiato negli appunti