Docker prune: liberare spazio su disco su un VPS
Scopri se immagini, container, cache di build o volumi occupano il disco del VPS, poi usa il prune più specifico per liberare spazio senza perdere dati.
Individua cosa occupa spazio su disco prima di eseguire qualsiasi prune
Docker occupa spazio su disco su un VPS in quattro aree: immagini, container arrestati, cache di build e volumi locali. Esegui prima docker system df per individuare quella che occupa lo spazio, quindi usa il comando prune più specifico in grado di liberarlo. L’ordine è importante, perché l’ultimo comando di questa guida, docker volume prune -a, elimina dati senza possibilità di annullamento.
Inizia dal filesystem, non da Docker.
df -h /
sudo du -xh --max-depth=1 /var/lib/docker | sort -hdf indica quanto è grave la situazione. du mostra dove si trova lo spazio occupato. Il flag -x mantiene du sullo stesso filesystem, quindi non segue un mount verso un volume separato e non conta lo spazio due volte. In questo caso sono importanti cinque directory: overlay2 contiene i layer delle immagini e dei container, volumes contiene i dati dei volumi, containers contiene i metadati dei container e i file di log, buildkit contiene la cache di build e image contiene i metadati dei layer.
Una nota su sudo e sui caratteri jolly della shell, perché sono una fonte frequente di problemi. /var/lib/docker appartiene a root e non è leggibile dall’utente normale, quindi ls /var/lib/docker restituisce Permission denied. Anche un comando come sudo du -sh /var/lib/docker/* non funziona, perché la shell espande * prima che venga eseguito sudo, ma non può leggere quella directory. Per questo tutti i comandi seguenti usano find o --max-depth invece di un carattere jolly.
Ora controlla la situazione dal punto di vista di Docker.
docker system dfTYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 24 6 12.4GB 8.91GB (71%)
Containers 31 6 1.42GB 1.39GB (97%)
Local Volumes 14 5 6.03GB 4.11GB (68%)
Build Cache 212 0 9.87GB 9.87GBQuesti valori provengono da una macchina specifica e non descrivono la tua. Osserva invece la struttura dei dati. TOTAL conta gli oggetti, ACTIVE conta quelli attualmente in uso e RECLAIMABLE è la stima di Docker dello spazio che un prune potrebbe liberare per quella riga.
Due aspetti di RECLAIMABLE possono creare confusione. Conta i layer condivisi delle immagini una volta per ogni immagine che li usa, quindi la riga delle immagini indica generalmente più spazio di quello che puoi effettivamente liberare. Inoltre, non include mai i file di log dei container, perché Docker non considera un file di log un oggetto recuperabile. Quando du mostra una directory molto più grande di quanto indicato da docker system df, la causa sono i file di log; più avanti trovi una sezione dedicata.
Aggiungi -v per ottenere il dettaglio dei singoli oggetti.
docker system df -vIl riepilogo viene suddiviso in una sezione per ogni tipo di oggetto. La sezione delle immagini aggiunge le colonne SHARED SIZE e UNIQUE SIZE, così puoi vedere quanto spazio occupa realmente una singola immagine. La sezione dei volumi aggiunge una colonna LINKS, che indica il numero di container collegati al volume. Ricorda LINKS, perché un valore pari a 0 è l’unico controllo su cui si basano i comandi di prune dei volumi.
Immagini dangling e immagini inutilizzate
Questi due termini sembrano intercambiabili, ma non lo sono. I filtri si comportano in modo diverso perché gli oggetti sono diversi.
Un'immagine dangling è un'immagine senza tag. In docker images viene visualizzata come <none>. Se esegui una ricostruzione, ne crei una a ogni operazione: docker build -t myapp:latest . sposta il tag myapp:latest sulla nuova immagine e quella precedente conserva tutti i suoi layer, ma perde il nome. Nessun oggetto la referenzia e nessun processo la rimuove automaticamente.
Un'immagine inutilizzata è un'immagine, con o senza tag, a cui nessun container fa attualmente riferimento. Un'immagine postgres:16 che hai scaricato il mese scorso e che non stai eseguendo in questo momento è inutilizzata, ma non è dangling.
docker image prune # dangling images only
docker image prune -a # every image no container refers toIl secondo comando chiede prima conferma.
WARNING! This will remove all images without at least one container associated to them.
Are you sure you want to continue? [y/N]Leggi attentamente quel prompt. "Associated to them" indica un oggetto container esistente, in esecuzione o arrestato. Se hai eseguito docker compose down, i container non esistono più. Di conseguenza, ogni immagine usata da quei servizi è ora inutilizzata e -a le elimina tutte. Non perdi nulla che non possa essere recuperato, ma il comando docker compose up -d successivo deve scaricare di nuovo o ricostruire tutto. Su un piccolo VPS, questo consuma banda e tempo di build. Per questo è utile sapere che cosa rimuove docker compose down e che cosa lascia in esecuzione stop prima di eliminare le immagini.
Un filtro esclude dall'operazione le immagini recenti.
docker image prune -a --filter "until=240h"Questo comando rimuove le immagini inutilizzate create più di 240 ore (10 giorni) fa e lascia inalterate quelle più recenti. Il valore until accetta una stringa di durata Go come 240h oppure un timestamp assoluto come 2026-08-01T00:00:00.
Che cos’è la cache di build e perché cresce senza limiti
BuildKit è il builder usato per impostazione predefinita da Docker per docker build e docker compose build a partire da Docker Engine 23.0. Memorizza nella cache il risultato di ogni passaggio di ogni Dockerfile che esegue e conserva questa cache in /var/lib/docker/buildkit. La cache permette alla seconda build di terminare in pochi secondi, quindi svolge correttamente il suo compito. Il problema è che, per impostazione predefinita, nessun processo elimina le voci obsolete. Se esegui cinquanta volte la build della stessa immagine con un passaggio COPY che cambia ogni volta, conservi cinquanta insiemi di layer.
La cache di build non è visibile a docker image prune. È un tipo di oggetto separato, con un comando dedicato.
docker builder prune # dangling cache
docker builder prune -a # all unused cache
docker builder prune --filter until=168h # cache untouched for 7 daysNessuno di questi comandi modifica le immagini o i dati. L’unico costo della cancellazione della cache di build è che la build successiva viene eseguita lentamente una sola volta. Su un VPS che ricostruisce regolarmente le immagini, Build Cache è spesso la voce più grande in docker system df; per questo è l’elemento di grandi dimensioni più sicuro da eliminare.
I comandi prune, ordinati dal meno rischioso al più distruttivo
Scorri questo elenco e interrompi la procedura non appena df -h / torna in condizioni normali. Al termine, ogni comando stampa una riga Total reclaimed space:.
docker container prunerimuove i container arrestati. Elimina anche i relativi writable layer, quindi tutti i dati scritti da un container al di fuori di un volume vengono eliminati insieme al container. I volumi non vengono modificati.docker image prunerimuove soltanto le immagini dangling. È il comando sulle immagini con il rischio più basso.docker builder prunerimuove la build cache dangling. L'effetto è una sola build più lenta.docker image prune -arimuove tutte le immagini a cui non fa riferimento alcun container. Sarà necessario scaricarle nuovamente o ricostruirle.docker system pruneesegue contemporaneamente le prime tre operazioni e aggiunge le reti inutilizzate.docker volume prunerimuove i volumi anonimi inutilizzati.docker volume prune -arimuove i volumi inutilizzati, inclusi quelli con nome. È il comando che elimina i database.
docker system prune dichiara il proprio ambito prima dell'esecuzione.
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- unused build cache
Are you sure you want to continue? [y/N]I volumi sono esclusi intenzionalmente da questo elenco. Aggiungendo --volumes si includono anche i volumi anonimi. Aggiungendo -a si estende l'operazione sulle immagini, dalle sole immagini dangling a tutte le immagini inutilizzate. Un docker system prune -a --volumes -f completo su un host di produzione è il modo in cui si perdono dati mentre si cerca di liberare spazio.
Perché la pulizia dei volumi elimina il database
Questa è la sezione da rileggere due volte.
Un volume è considerato inutilizzato quando nessun container è collegato a esso. Questo è l'unico controllo eseguito. Docker non verifica se il volume è vuoto, se un file compose lo dichiara ancora o se contiene l'unica copia del database. LINKS 0 in docker system df -v significa che il volume può essere eliminato, e non significa altro.
Ora esegui due normali operazioni in sequenza. Usi docker compose down per riavviare ordinatamente uno stack. Questo comando rimuove i container e lascia invariati i volumi denominati, esattamente come indicato nella documentazione. A questo punto il volume di Postgres non è collegato a nulla. Dieci minuti dopo esegui docker volume prune -a per liberare spazio e il database scompare. Entrambi i comandi hanno funzionato correttamente. È stata la sequenza a distruggere i dati.
A partire da Docker Engine 23.0 (versione API 1.42), il comando senza opzioni è più limitato rispetto al passato.
WARNING! This will remove anonymous local volumes not used by at least one container.Un volume anonimo è un volume creato automaticamente da Docker, in genere perché un'immagine dichiara VOLUME e non gli hai assegnato un nome. Di norma contiene dati che non avevi chiesto di conservare. Un volume denominato, cioè quello inserito nel file compose, viene rimosso solo quando aggiungi -a. Le versioni precedenti di Docker rimuovevano entrambi con il comando senza opzioni. Non fare quindi affidamento su abitudini apprese su un sistema che nel frattempo hai aggiornato. La differenza è comprensibile solo dopo aver visto come differiscono i volumi denominati dai bind mount, perché un bind mount non è affatto un volume Docker e nessun comando prune lo toccherà mai.
Controlla prima di eliminare. Sostituisci myapp_pgdata con il nome del volume che vuoi verificare.
docker volume ls -f dangling=true
docker volume inspect myapp_pgdata
sudo ls -la /var/lib/docker/volumes/myapp_pgdata/_dataIl filtro dangling=true applicato a un volume significa non referenziato, non vuoto. L'elenco _data mostra il contenuto effettivo. Se trovi una directory pgdata o mysql al suo interno, fermati e crea una copia prima di procedere. La stessa distruzione può avvenire tramite docker compose down -v, che rimuove tutti i volumi dichiarati dal file compose senza chiedere una conferma.
Un volume è l'unico elemento di un host Docker che una ricostruzione non può ricreare. Per questo i dati dei volumi devono essere inclusi in un backup restic eseguito al di fuori del server, in modo che un flag digitato per errore non possa raggiungerli.
Quando nulla viene eliminato: i file di log dei container
Hai eliminato tutto ciò che era possibile eliminare, docker system df mostra quasi nulla di recuperabile, ma il disco è ancora pieno. Controlla i log.
sudo find /var/lib/docker/containers -name '*-json.log' -exec du -h {} + | sort -h | tail -10Ogni container scrive l’output standard e l’errore standard in un file JSON sotto /var/lib/docker/containers/. In un’installazione predefinita max-size non è impostato, quindi non esiste alcun limite. Un singolo container bloccato in un ciclo di crash può quindi scrivere finché la partizione non si riempie. Nessun comando prune rimuove questi file, perché i container che li producono sono in esecuzione e, per definizione, non possono essere eliminati.
Non eliminare il file. Eseguire rm su un file di log aperto non libera spazio, perché il daemon Docker mantiene aperto un file descriptor e il kernel conserva quei blocchi allocati finché quel riferimento non viene chiuso. df non si sposterà affatto. Usa invece truncate: mantiene lo stesso inode e consente al daemon di continuare a scrivere.
sudo find /var/lib/docker/containers -name '*-json.log' -exec truncate -s 0 {} +
df -h /È una soluzione temporanea. docker logs per quei container ora non restituisce nulla, ma i file ricominciano subito a crescere. La soluzione corretta è la rotazione dei log, descritta nella sezione successiva.
Misurare prima e dopo, ogni volta
Non dedurre mai l'effetto di un prune. Esegui una misurazione, un solo comando, quindi una seconda misurazione.
df -h /
docker system df
docker builder prune -f --filter until=168h
docker image prune -f
docker system df
df -h /Confronta i due output di df. Questo è l'unico numero che determina se il server continua a erogare il servizio. docker system df indica quindi quale riga è effettivamente cambiata, mentre ogni prune stampa il proprio valore Total reclaimed space:.
Se df non è cambiato, ma docker system df indica che è stato liberato spazio, un file aperto mantiene occupati i blocchi eliminati. Questo corrisponde al problema dei file di log descritto sopra. Se entrambi sono cambiati e il disco torna pieno entro un giorno, il problema è la crescita dei dati, non la pulizia. La soluzione consiste nella rotazione dei log e in un job pianificato.
Come evitare che il disco si riempia di nuovo
Limita le dimensioni dei log. Crea o modifica /etc/docker/daemon.json.
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}In questo modo ogni container viene limitato a 30 MB di log. Ogni valore in log-opts deve essere una stringa, compresi quelli numerici. Verifica che il file venga analizzato correttamente prima di riavviare, perché un daemon.json non valido impedisce completamente l'avvio del daemon e arresta anche tutti i container.
python3 -m json.tool /etc/docker/daemon.json
sudo systemctl restart docker
docker info | grep -i 'logging driver'docker info dovrebbe ora restituire Logging Driver: json-file. I limiti sono visibili nella sezione LogConfig di docker inspect per un container creato dopo il riavvio. Questo è il dettaglio importante: l'impostazione si applica solo ai nuovi container. I container esistenti mantengono la configurazione con cui sono stati creati, quindi devi ricrearli.
docker compose up -d --force-recreateLo stesso limite può essere impostato per singolo servizio in un file Compose. Questa è la scelta migliore quando un servizio particolarmente rumoroso richiede un valore diverso.
services:
app:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"Pianifica una pulizia mirata. Eseguila ogni settimana, limitandola alle immagini dangling e alla vecchia cache di build. Non inserire mai -a o --volumes in un job pianificato, perché un job eseguito mentre uno stack è arrestato eliminerà le immagini di quello stack e, con --volumes, inizierà a operare sui tuoi dati.
sudo tee /etc/cron.weekly/docker-prune >/dev/null <<'EOF'
#!/bin/sh
docker image prune -f
docker builder prune -f --filter until=168h
EOF
sudo chmod +x /etc/cron.weekly/docker-prune
sudo /etc/cron.weekly/docker-pruneL'ultima riga esegue manualmente lo script una volta, così puoi visualizzarne l'output prima che venga eseguito automaticamente. Il file deve essere eseguibile e il suo nome non deve contenere un punto, perché run-parts ignora tutto ciò che non è eseguibile e tutto ciò che ha un'estensione.
Imposta un avviso sullo spazio libero. Una pulizia eseguita dopo che il disco si è riempito è un intervento di ripristino. Un avviso all'80 percento è prevenzione.
usage=$(df --output=pcent / | tr -dc '0-9')
[ "$usage" -ge 80 ] && echo "root filesystem at ${usage} percent full"Inseriscilo in cron usando il notifier che già utilizzi. Lo spazio libero è solo metà del problema, quindi associa l'avviso a monitoraggio dello stato del disco sul tuo VPS, perché un disco guasto e un disco pieno bloccano entrambi i container, ma richiedono correzioni diverse.
Tutto quanto sopra presuppone un'installazione standard con la directory radice dei dati in /var/lib/docker. Se l'hai spostata usando la chiave data-root in daemon.json, sostituisci il percorso in ogni comando. Configurare correttamente questa struttura su un sistema nuovo fa parte della configurazione di Docker su un VPS ed è molto più semplice decidere prima di avere 40 GB di container nella partizione sbagliata.
FAQ
Docker system prune elimina i miei volumi?
No. Il comando senza opzioni rimuove i container arrestati, le reti inutilizzate, le immagini dangling e la cache di build inutilizzata. Il prompt di conferma elenca esattamente questi elementi. I volumi vengono inclusi solo quando aggiungi --volumes. A partire da Docker Engine 23.0, questa opzione riguarda i volumi anonimi, non quelli con nome. I volumi con nome vengono rimossi da docker volume prune -a e da docker compose down -v. Questi sono i due comandi da usare con cautela.
Perché il disco è ancora pieno dopo aver eseguito docker prune?
Le cause più comuni sono due. La prima sono i file di log dei container sotto /var/lib/docker/containers/. Nessun comando prune li modifica e possono crescere senza limiti finché non imposti max-size. La seconda è un file eliminato che un processo mantiene aperto. Se hai rimosso un log con rm mentre il relativo container era in esecuzione, il daemon conserva il file descriptor e il kernel non rilascia i blocchi. Di conseguenza, df non mostra alcuna variazione. Confronta sudo du -xh --max-depth=1 /var/lib/docker con docker system df per capire quale delle due situazioni si applica.
Qual è la differenza tra docker image prune e docker image prune -a?
Il comando senza opzioni rimuove solo le immagini dangling, cioè le immagini che hanno perso il tag, quasi sempre a causa di una rebuild. La forma -a rimuove tutte le immagini a cui nessun container esistente fa riferimento, incluse le immagini con tag che hai scaricato intenzionalmente. Dopo un docker compose down i container non esistono più, quindi -a rimuoverà anche le immagini utilizzate da quello stack. Nulla viene perso definitivamente, perché al successivo avvio le immagini vengono scaricate o ricostruite. Tuttavia, con una connessione lenta, l'operazione può richiedere molto tempo.
Come posso impedire che i log di Docker riempiano il disco?
Imposta max-size e max-file sotto log-opts in /etc/docker/daemon.json, quindi riavvia il daemon con sudo systemctl restart docker. L'impostazione si applica solo ai container creati dopo quel riavvio. Ricrea quindi quelli già in esecuzione con docker compose up -d --force-recreate. Puoi impostare le stesse due opzioni per ogni servizio in un file Compose, sotto una chiave logging. Questa soluzione è utile quando un servizio genera molti più log degli altri.
È sicuro eseguire docker system prune tramite un cron job?
Il comando docker system prune -f senza opzioni è sicuro su un host in cui tutti gli stack restano attivi. Tuttavia, rimuove i container arrestati. Pertanto, eliminerà anche un container che hai arrestato intenzionalmente e che intendevi riavviare in seguito. Il job pianificato più sicuro è docker image prune -f insieme a docker builder prune -f --filter until=168h. Questa combinazione libera le due categorie che crescono più rapidamente e non può modificare alcun volume. Non pianificare mai -a o --volumes.