ZFS su VPS: quanta RAM consuma davvero l’ARC?
Checksum, snapshot, replica con send/receive e compressione hanno un costo: scopri come valutare il consumo dell’ARC su VPS da 2 o 4 GB.
Cosa offre ZFS e quali sono i requisiti
ZFS su FreeBSD e Linux utilizza ormai un’unica base di codice, OpenZFS, quindi le funzionalità sono le stesse su entrambi i sistemi. Un server che esegue ZFS offre dati con checksum, snapshot che non consumano spazio finché i dati non cambiano, replica con zfs send e compressione attivabile impostando una proprietà. Il requisito principale è la memoria: per impostazione predefinita, l’ARC (adaptive replacement cache) utilizza una parte consistente della RAM. Su un VPS (virtual private server) da 2 GB o 4 GB, quella memoria è esattamente quella richiesta dall’applicazione.
Questa guida valuta ZFS su un VPS a noleggio con uno o due dischi virtuali, non su un sistema di storage con quaranta alloggiamenti per unità. Le funzionalità che restano utili in questo contesto sono quelle che meritano il tuo tempo. È importante conoscere prima di creare un pool anche gli aspetti che non mantengono la loro utilità in questo scenario.
OpenZFS su FreeBSD e Linux: una base di codice, due modalità di distribuzione
FreeBSD include ZFS nel sistema base da FreeBSD 7.0, rilasciato nel 2008, inizialmente come funzionalità sperimentale. Da OpenZFS 2.0, rilasciato a dicembre 2020, FreeBSD e Linux compilano dallo stesso albero dei sorgenti. Di conseguenza, zfs e zpool si comportano allo stesso modo su entrambe le piattaforme e un pool creato su una piattaforma può essere importato sull'altra.
Il motivo per cui ZFS è un pacchetto su Linux e fa parte del sistema base su FreeBSD è la licenza. OpenZFS è distribuito con la CDDL (common development and distribution license). Il kernel Linux è distribuito con la GPL (general public license) versione 2. Il progetto del kernel considera le due licenze incompatibili. Per questo il codice di ZFS non viene integrato nel ramo principale di Linux e ogni distribuzione decide come distribuirlo. FreeBSD non presenta questo conflitto, quindi ZFS è incluso direttamente. Questa è l'intera spiegazione pratica: cambia il metodo di distribuzione, ma non c'è alcuna scelta tecnica da sostenere.
SSD Nodes non offre immagini FreeBSD. Su un server noleggiato qui si applica quindi la parte della guida relativa a Linux. Se esegui FreeBSD altrove, un server FreeBSD include ZFS senza dover compilare moduli e senza dover gestire aggiornamenti del kernel per mantenerlo operativo.
Installare ZFS e creare un pool
Su Ubuntu il modulo è incluso nei pacchetti del kernel, quindi è sufficiente installare i comandi.
sudo apt update
sudo apt install -y zfsutils-linux
zfs versionzfs version stampa due righe: la versione dello spazio utente e quella del modulo del kernel. Se viene visualizzata una sola riga, il modulo non è stato caricato. Il pacchetto si trova nel componente universe, che le immagini Ubuntu Server abilitano per impostazione predefinita; se apt non riesce a trovarlo, eseguire prima sudo add-apt-repository universe.
Su Debian i pacchetti si trovano nel componente contrib e il modulo viene compilato sulla macchina da DKMS (supporto per i moduli dinamici del kernel). Aggiungere contrib alla riga Components: in /etc/apt/sources.list.d/debian.sources, eseguire sudo apt update, quindi:
sudo apt install -y linux-headers-$(dpkg --print-architecture) zfs-dkms zfsutils-linuxL'installazione compila il modulo e stampa Building initial module for 6.12.0-...; l'operazione richiede alcuni minuti. Questo comporta che ogni aggiornamento del kernel ricompila il modulo. Se la compilazione non riesce, il pool non viene importato finché il problema non viene risolto.
Su FreeBSD non è installato nulla. Abilitare il servizio e avviarlo.
sysrc zfs_enable=YES
service zfs startOra creare il pool. Controllare prima i percorsi stabili dei dispositivi, perché /dev/vdb li assegna nell'ordine di rilevamento e questo ordine può cambiare quando si collega un altro volume.
ls -l /dev/disk/by-id/
sudo zpool create -o ashift=12 tank /dev/disk/by-id/virtio-abc123def456
zpool status tankzpool status dovrebbe stampare state: ONLINE, con il dispositivo elencato sotto tank. ashift=12 imposta a 4 KiB la dimensione minima dei blocchi del pool. Questo valore corrisponde agli SSD attuali e non può essere modificato dopo la creazione.
La maggior parte delle immagini noleggiate si avvia da una partizione root ext4. In questo caso ZFS viene usato per un pool di dati su un secondo volume, non per il filesystem root. Verificare che il dispositivo sia quello previsto prima di usarlo, perché verificare il disco NVMe acquistato richiede un minuto, mentre una ricostruzione richiede un pomeriggio.
I checksum riparano solo quando il pool dispone di ridondanza
Ogni blocco scritto da ZFS include un checksum, che viene verificato a ogni lettura. Il rilevamento funziona sempre. La riparazione richiede una seconda copia.
Su un pool composto da un solo disco, ZFS segnala il problema e si ferma. zpool status -v lo mostra in questo modo:
status: One or more devices has experienced an error resulting in data
corruption.
action: Restore the file in question if possible. Otherwise restore the
entire pool from backup.
errors: Permanent errors have been detected in the following files:
/tank/data/archive.tarViene indicato il file danneggiato. ext4 avrebbe restituito quei byte senza segnalare nulla, quindi questo controllo è già utile. ZFS non può comunque correggerli, perché nel pool non esiste una seconda copia da cui recuperarli.
Con un mirror, la stessa lettura viene servita dalla copia integra, il blocco danneggiato viene riscritto e l'evento compare nella colonna CKSUM di zpool status. Questa è la riparazione automatica e richiede due dispositivi.
sudo zpool create -o ashift=12 tank mirror /dev/disk/by-id/DISK1 /dev/disk/by-id/DISK2Su una VPS, lo storage dell'host è in genere già ridondante, spesso con RAID 10 sul hypervisor. Questo protegge dalla perdita di un disco. Non segnala però quando un blocco viene restituito con dati errati, perché l'array non può sapere quale copia sia corretta. ZFS lo sa perché confronta i dati con un checksum che ha scritto autonomamente.
Se hai un disco virtuale e vuoi ottenere una certa capacità di riparazione, sudo zfs set copies=2 tank/important memorizza due copie di ogni blocco di quel dataset sullo stesso disco. In questo modo lo spazio occupato dal dataset raddoppia, un blocco danneggiato può essere recuperato, ma la funzione non serve se scompare l'intero volume.
Uno scrub legge tutto il pool e verifica i dati.
sudo zpool scrub tank
zpool status tankUn pool integro termina con una riga come scan: scrub repaired 0B in 00:04:11 with 0 errors. Pianificalo a intervalli regolari; una volta al mese è sufficiente per un pool di piccole dimensioni.
systemctl list-unit-files 'zfs-scrub*'
sudo systemctl enable --now zfs-scrub-monthly@tank.timerI dataset sono l’unità delle policy
Un dataset è un filesystem all’interno del pool e crearne uno ha un costo ridotto, quindi conviene crearne uno per ogni attività. Le proprietà vengono ereditate dal pool verso il basso: si imposta un valore predefinito una sola volta e lo si sovrascrive dove serve. Su FreeBSD i jail vengono normalmente eseguiti in questo modo, con un dataset per ogni jail, così è possibile creare snapshot e ripristinare singolarmente un jail. Questo rientra tra gli aspetti che distinguono un jail da un container Docker.
sudo zfs create tank/data
sudo zfs create tank/pg
sudo zfs set compression=lz4 tank
sudo zfs set atime=off tank
sudo zfs set quota=20G tank/data
sudo zfs set recordsize=16K tank/pg
zfs get -r compression,compressratio,quota tankLa compressione è la proprietà che molti omettono per prudenza, ma è una scelta controproducente. lz4 richiede una quantità ridotta di CPU e diminuisce i byte da scrivere sul disco, quindi sui dati comprimibili rende generalmente più veloci le operazioni di lettura e scrittura. zstd comprime di più a fronte di un maggiore utilizzo della CPU, quindi è adatta a log e archivi che vengono letti raramente. Verifica il risultato effettivo con zfs get compressratio tank e ricorda che il rapporto considera soltanto i dati scritti dopo l’impostazione della proprietà.
recordsize è la dimensione massima del blocco scritto da un dataset, pari a 128K per impostazione predefinita. Un database che scrive pagine da 8 KiB in record da 128 KiB trasforma una piccola scrittura nella lettura dell’intero record, in una modifica e nella successiva riscrittura. Imposta recordsize=16K sul dataset del database prima di caricare i dati, perché la proprietà si applica solo ai blocchi scritti successivamente.
quota impedisce a un dataset di riempire il pool. Un pool ZFS quasi pieno al 100% diventa lento e difficile da ripulire, quindi è opportuno lasciare spazio libero intenzionalmente.
Gli snapshot non occupano spazio finché i dati non cambiano
ZFS non sovrascrive mai un blocco in uso. Scrive un nuovo blocco e aggiorna i puntatori: questo è il significato di copy-on-write. Uno snapshot indica di conservare i blocchi a cui il dataset punta in quel momento, quindi la sua creazione è istantanea e non occupa spazio.
sudo zfs snapshot tank/data@2026-08-11
zfs list -t snapshot -o name,used,refer -r tank/dataLa colonna USED di uno snapshot indica lo spazio occupato esclusivamente da quello snapshot. All'inizio è quasi pari a zero e aumenta quando modifichi o elimini i dati, perché i vecchi blocchi non possono più essere rilasciati.
Per recuperare un file non serve alcuna procedura di ripristino.
ls /tank/data/.zfs/snapshot/
cp /tank/data/.zfs/snapshot/2026-08-11/notes.txt /tank/data/notes.txtLa directory .zfs è nascosta anche a ls -a finché non esegui sudo zfs set snapdir=visible tank/data. Crea lo snapshot prima di averne bisogno, perché senza uno snapshot un semplice rm -rf ti porta nel percorso di ripristino ext4, che inizia con lo smontaggio del disco e da quel punto la situazione peggiora.
Il rollback elimina tutto ciò che è stato scritto dopo lo snapshot.
sudo zfs rollback tank/data@2026-08-11L'operazione viene rifiutata se esistono snapshot più recenti, mentre -r elimina questi snapshot per consentirne l'esecuzione. Controlla due volte il nome del dataset prima di premere Invio.
Uno snapshot non è un backup. Si trova nello stesso pool, sullo stesso volume e sullo stesso server. Un volume guasto o un zpool destroy porta via gli snapshot insieme ai dati. Gli snapshot proteggono dagli effetti del tuo rm e da un aggiornamento problematico, coprendo molti incidenti reali, ma non proteggono da ciò che accade al pool stesso. La spiegazione completa è disponibile qui: perché uno snapshot di un VPS non è un backup.
Inviare e ricevere: replica con un solo comando
zfs send converte uno snapshot in un flusso di byte sull'output standard, mentre zfs receive riconverte quel flusso in un dataset. La prima copia è un invio completo.
sudo zfs snapshot tank/data@daily-2026-08-11
sudo zfs send tank/data@daily-2026-08-11 | ssh backup.example.com "sudo zfs recv -F backup/data"In seguito, invia soltanto le modifiche tra due snapshot.
sudo zfs snapshot tank/data@daily-2026-08-12
sudo zfs send -i tank/data@daily-2026-08-11 tank/data@daily-2026-08-12 | ssh backup.example.com "sudo zfs recv backup/data"Il lato ricevente deve conservare lo snapshot da cui stai effettuando l'invio. In caso contrario, la ricezione si interrompe con cannot receive incremental stream: most recent snapshot of backup/data does not match incremental source, perché ZFS non dispone di una base a cui applicare la differenza. Invia da uno snapshot conservato su entrambi i lati oppure riparti con un invio completo.
Concedi i diritti sulla destinazione invece di usare root remoto: sudo zfs allow -u backupuser create,mount,receive backup/data.
Si tratta di un vero backup off-site, con una condizione. L'estremità remota deve essere un pool ZFS, perché l'object storage non può ricevere un flusso. Se la destinazione è uno storage compatibile con S3 o un host Linux semplice, usa uno strumento che supporti quel tipo di destinazione; i backup restic da un VPS illustrano questa soluzione.
Perché ZFS usa così tanta RAM? La ARC
La ARC (adaptive replacement cache) è la cache di lettura di ZFS. Risiede nella memoria del kernel, non nella normale page cache di Linux, quindi free -h non la riporta nella voce buff/cache. Viene conteggiata come memoria in uso. Un sistema ZFS che sembra quasi pieno ha in genere la cache già popolata; questo spiega la maggior parte delle segnalazioni secondo cui «ZFS ha consumato tutta la RAM».
Il limite predefinito è volutamente elevato. OpenZFS 2.3 imposta la dimensione massima della ARC sul valore maggiore tra la RAM meno 1 GiB e 5/8 della RAM. OpenZFS 2.2 e versioni precedenti usavano metà della RAM su Linux, mentre FreeBSD applicava già la regola più recente. Eseguire zfs version per verificare quale regola si applica al sistema.
The data behind this chart
[
{
"label": "2 GB VPS",
"openzfs_2_2_linux_gib": 1,
"openzfs_2_3_gib": 1.25
},
{
"label": "4 GB VPS",
"openzfs_2_2_linux_gib": 2,
"openzfs_2_3_gib": 3
},
{
"label": "8 GB VPS",
"openzfs_2_2_linux_gib": 4,
"openzfs_2_3_gib": 7
},
{
"label": "16 GB VPS",
"openzfs_2_2_linux_gib": 8,
"openzfs_2_3_gib": 15
}
]Questi valori derivano dalla regola predefinita documentata applicata alle dimensioni comuni delle istanze; non sono misurazioni effettuate su un sistema in esecuzione. Su un'istanza da 4 GB, la regola della versione 2.3 consente una ARC di 3 GiB. Lo stesso sistema con la versione 2.2 si ferma a 2 GiB. Un'istanza da 2 GB con la regola della versione 2.3 consente comunque 1.25 GiB. L'applicazione riceve la memoria rimanente.
Leggere i valori reali direttamente dal proprio server invece di affidarsi alla tabella:
grep -E '^(size|c_max) ' /proc/spl/kstat/zfs/arcstats
arc_summary | head -n 20La terza colonna è espressa in byte. c_max è il limite massimo attualmente applicato, mentre size è la quantità di memoria attualmente occupata dalla ARC.
La ARC restituisce effettivamente memoria. Il kernel segnala la pressione sulla memoria e la ARC si riduce. Il problema è il momento in cui avviene questa riduzione: è attivata dalla pressione sulla memoria, quindi un processo che richiede in una sola volta diverse centinaia di MiB può incontrare l'OOM (out of memory) killer mentre la ARC sta ancora rilasciando memoria. Su un sistema da 2 GB che esegue un database e un server web, non è un evento raro. Il manuale di OpenZFS indica la stessa limitazione per le modifiche manuali: la riduzione del limite «non farà diminuire la ARC in assenza di pressione sulla memoria che induca tale riduzione».
Come limitare l’ARC su una VPS di piccole dimensioni
Determina prima la memoria necessaria per il carico di lavoro. Somma la memoria richiesta dal database e dall’applicazione, mantieni un margine per il sistema operativo e assegna il resto all’ARC. Su un’istanza da 4 GB che esegue Postgres e un’applicazione web, 512 MiB-1 GiB di ARC sono un punto di partenza ragionevole.
Imposta il valore mentre il sistema è in esecuzione, in byte. In questo caso è 1 GiB.
echo 1073741824 | sudo tee /sys/module/zfs/parameters/zfs_arc_maxRendi persistente l’impostazione dopo un riavvio.
echo 'options zfs zfs_arc_max=1073741824' | sudo tee /etc/modprobe.d/zfs.conf
sudo update-initramfs -uIl passaggio relativo a initramfs è importante perché il modulo può essere caricato da initramfs prima del montaggio del filesystem root. In tal caso non leggerebbe mai il file appena scritto. Dopo il riavvio, verifica il valore con la riga c_max di arcstats.
Il manuale stesso indica due aspetti da considerare. Non puoi riportare il valore a 0 mentre il sistema è in esecuzione. Per annullare l’impostazione devi quindi modificare il file e riavviare il sistema. Inoltre, ridurre il valore non ridimensiona immediatamente un ARC già grande.
Su FreeBSD lo stesso limite è un sysctl in vfs.zfs.arc. Esegui sysctl vfs.zfs.arc per visualizzare i valori correnti e il nome esatto usato dalla tua versione, quindi scrivi il valore massimo in /boot/loader.conf.
Per un server di piccole dimensioni valgono altre due regole sulla memoria. Lascia disattivata la deduplicazione, perché la tabella di deduplicazione risiede in memoria e la regola empirica comunemente citata prevede da 1 a 3 GB di RAM per ogni TB di dati univoci. Non usare inoltre un zvol come swap (un dispositivo a blocchi ricavato dal pool), perché lo swapping attraverso il filesystem che sta cercando di liberare memoria può causare un deadlock del sistema. Mantieni lo swap su una partizione normale o su un file di swap esterno al pool.
Quando ext4 o XFS con restic sono la scelta migliore
ZFS è vantaggioso su un server con memoria disponibile e un secondo volume. Negli altri casi, un filesystem tradizionale abbinato a uno strumento di backup affidabile è la scelta migliore. Scegli ext4 o XFS quando:
- L'istanza dispone di 2 GB o 4 GB di RAM e il carico di lavoro richiede tutta la memoria disponibile.
- È presente un solo disco virtuale e non esiste una seconda copia, quindi ZFS consente di rilevare i problemi ma non di correggerli.
- La destinazione dei backup è un object storage o un host Linux tradizionale, quindi nessuno dei due può ricevere uno stream
zfs send. - Usi Debian con DKMS e non puoi permetterti un aggiornamento del kernel che lasci il modulo non compilato.
- Ti serve ZFS sul filesystem root, ma le immagini del provider offrono soltanto ext4.
Mantieni ZFS quando hai un volume dati separato, RAM disponibile (8 GB o più sono una quantità adeguata) e un piano che utilizzi snapshot e zfs send, invece di limitarsi ad abilitarli. In tutti gli altri casi, ext4 con restic che scrive backup cifrati e deduplicati su uno storage non controllato dal server offre gran parte degli stessi vantaggi senza consumare memoria.
Modalità di errore e stringhe visualizzate
Il pool non è più disponibile dopo un riavvio. zpool status stampa no pools available. Il servizio di importazione legge /etc/zfs/zpool.cache, quindi un pool assente da quel file non viene mai importato all'avvio. sudo zpool import elenca i pool importabili, sudo zpool import tank lo importa nuovamente e sudo zpool set cachefile=/etc/zfs/zpool.cache tank rende permanente l'impostazione. Un pool non esportato correttamente da un altro sistema restituisce cannot import 'tank': pool may be in use from other system; sudo zpool import -f tank forza l'importazione quando hai verificato che nessun altro host lo stia utilizzando.
modprobe: FATAL: Module zfs not found in directory /lib/modules/6.12.0-... su Debian dopo un aggiornamento del kernel. DKMS non è riuscito a compilare il modulo per il nuovo kernel, in genere perché non sono installati gli header corrispondenti. dkms status mostra quale modulo è compilato per ciascun kernel. sudo apt install -y linux-headers-$(uname -r), quindi, sudo dkms autoinstall lo ricompila e sudo zpool import tank ripristina il pool.
Il pool è pieno, ma hai eliminato i file. I dati eliminati restano sul disco finché uno snapshot continua a farvi riferimento; per questo du e df mostrano valori diversi. zfs list -o space -r tank suddivide l'utilizzo in USEDDS e USEDSNAP; un valore USEDSNAP elevato conferma la causa. Elimina i vecchi snapshot con sudo zfs destroy tank/data@2026-06-01 per liberare lo spazio.
I conteggi CKSUM aumentano in zpool status. Un componente sottostante a ZFS ha restituito dati non validi. In un mirror, il conteggio è un avviso e il blocco è stato riparato. In un pool composto da un solo disco, il file è perso; zpool status -v ne indica il nome e devi ripristinare quel file da un backup che non si trova in questo pool.
Il server è lento e utilizza la swap. Limita l'ARC come descritto sopra, quindi esegui arc_summary e controlla il rapporto di hit. Se l'ARC è troppo piccolo per contenere il working set, ogni lettura viene eseguita dal disco. A quel punto un filesystem tradizionale che utilizza la page cache offrirebbe prestazioni migliori.
FAQ
Quanta RAM richiede ZFS su un VPS?
ZFS funziona su un'istanza da 2 GB. La domanda reale è quanta memoria resta disponibile per l'applicazione. Senza ottimizzazione, OpenZFS 2.3 consente ad ARC di crescere fino al maggiore tra RAM meno 1 GiB e 5/8 della RAM; quindi, su un server da 4 GB, la cache può utilizzare 3 GiB. Impostare zfs_arc_max su un valore che il carico di lavoro può lasciare disponibile, quindi confermare il valore leggendo la riga c_max da /proc/spl/kstat/zfs/arcstats.
Uno snapshot ZFS è un backup?
No. Uno snapshot risiede nello stesso pool dei dati. Sopravvive a un rm errato e a un aggiornamento non riuscito, ma viene perso se il pool o l'istanza diventano indisponibili. Per trasformarlo in un backup, inviarlo a un altro computer con zfs send oppure usare uno strumento di backup che scriva su uno storage non gestito da questo server.
ZFS funziona nello stesso modo su FreeBSD e Linux?
Dal rilascio di OpenZFS 2.0, nel dicembre 2020, la base di codice, i comandi e il formato su disco sono gli stessi, e i pool possono essere spostati tra i due sistemi. La differenza riguarda il packaging. FreeBSD include ZFS nel sistema di base. Su Linux ogni distribuzione decide come distribuirlo: Ubuntu compila il modulo nei propri pacchetti del kernel, mentre Debian lo compila sul computer usando DKMS. Di conseguenza, un aggiornamento del kernel può lasciare il sistema senza modulo finché la ricompilazione non termina correttamente.
ZFS può riparare la corruzione su un VPS con un solo disco?
Rileva la corruzione e indica il file, ma non può ripararla, perché la riparazione richiede una seconda copia del blocco. zfs set copies=2 su un dataset fornisce questa seconda copia, raddoppiando lo spazio utilizzato; consente di gestire un blocco danneggiato, ma non un volume perso. Un mirror su due volumi è la soluzione che consente una riparazione effettiva.
La compressione rallenta il server?
lz4 di solito rende il server più veloce. I blocchi compressi richiedono meno byte da scrivere e da leggere, mentre il costo CPU per blocco è ridotto rispetto al tempo risparmiato sul disco. Impostare compression=lz4 alla radice del pool, così ogni dataset eredita l'impostazione, quindi controllare zfs get compressratio tank dopo aver scritto dati reali.