Rotazione dei log Docker: evitare che i log riempiano il disco del VPS

12 min di lettura·Matthieu·linuxdisk-managementloggingdocker-composedocker|

Il log driver predefinito di Docker non ha limiti di dimensione. Un singolo container può riempire un disco da 50 GB in pochi giorni. Questo tutorial configura la rotazione globale dei log, override per servizio in Compose, pulizia automatica e monitoraggio dello spazio disco.

La configurazione di logging predefinita di Docker non prevede alcun limite di dimensione. Ogni riga scritta dai container su stdout o stderr viene salvata su disco a tempo indeterminato. Su un VPS con 25 GB o 50 GB di spazio, un singolo container particolarmente verboso può esaurire tutto lo spazio disponibile in pochi giorni.

Questo tutorial risolve il problema. Si configurerà la rotazione globale dei log, si imposteranno limiti per servizio in Docker Compose, si automatizzerà la pulizia del disco e si attiverà il monitoraggio per evitare sorprese.

Tutti i comandi sono testati su Debian 12 e Ubuntu 24.04 con Docker Engine 28.x/29.x.

Prerequisiti:

  • Un VPS con Debian 12 o Ubuntu 24.04 e Docker installato
  • Accesso SSH con un utente sudo
  • Familiarità di base con Docker e Docker Compose

Perché i log dei container Docker riempiono il disco?

Il log driver predefinito di Docker è json-file. Cattura tutto ciò che un container scrive su stdout e stderr, salvandolo come JSON in /var/lib/docker/containers/<container-id>/<container-id>-json.log. Per impostazione predefinita non esiste una dimensione massima né una rotazione. Il file cresce fino a riempire il disco.

Un'applicazione Node.js con logging a livello INFO produce circa 50 MB al giorno. Un reverse proxy con traffico moderato può generare 200 MB o più. Su un VPS da 50 GB, un singolo container non gestito può consumare tutto lo spazio libero in poche settimane.

Quando il disco si riempie, tutto si blocca contemporaneamente: i container non possono scrivere, i database vanno in crash, le sessioni SSH possono bloccarsi e non è più possibile nemmeno accedere per risolvere il problema.

Come verificare l'uso attuale del disco

Prima di modificare qualsiasi cosa, è bene misurare la situazione:

df -h /var/lib/docker

Questo mostra quanto spazio occupa la directory dati di Docker. Poi si può ottenere un dettaglio specifico:

docker system df

Output atteso:

TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          5         3         1.2GB     450MB (37%)
Containers      8         4         3.8GB     3.1GB (81%)
Local Volumes   3         2         500MB     120MB (24%)
Build Cache     12        0         800MB     800MB (100%)

La riga "Containers" mostra la dimensione dei file di log. Se quel numero è sproporzionatamente grande, il problema sono i log.

Per un dettaglio per singolo container:

docker system df -v

Questo elenca ogni container con la dimensione dei suoi log. Permette di individuare i responsabili.

Come trovare direttamente i file di log più grandi

Se docker system df conferma il problema, si possono individuare esattamente i log che consumano più spazio:

sudo find /var/lib/docker/containers/ -name "*-json.log" -exec ls -sh {} + | sort -rh | head -10

Questo elenca i 10 file di log più grandi con le rispettive dimensioni.

Triage d'emergenza: recuperare spazio disco subito

Se il disco è già pieno o quasi pieno, bisogna risolvere il problema immediato prima di configurare la rotazione.

Troncare il file di log di un container specifico (senza fermarlo):

sudo truncate -s 0 /var/lib/docker/containers/<container-id>/<container-id>-json.log

Sostituire <container-id> con l'ID effettivo del container ottenuto da docker ps --no-trunc -q.

Per troncare tutti i file di log Docker contemporaneamente:

sudo sh -c 'truncate -s 0 /var/lib/docker/containers/*/*-json.log'

Verificare che lo spazio sia stato recuperato:

df -h /var/lib/docker

Questa è una soluzione temporanea. I log ricominceranno a crescere. Le sezioni successive rendono la correzione permanente.

Come configurare la rotazione dei log nel daemon.json di Docker?

Il file daemon.json imposta le opzioni di logging predefinite per tutti i nuovi container. Il driver json-file di Docker supporta max-size (dimensione massima per file di log prima della rotazione) e max-file (numero di file ruotati da conservare). Tutti i valori in log-opts devono essere stringhe, anche i numeri.

Creare o modificare la configurazione del daemon:

sudo nano /etc/docker/daemon.json

Se il file non esiste, crearlo. Se contiene già configurazioni (come registry personalizzati o DNS), aggiungere le chiavi log-driver e log-opts accanto a quelle esistenti.

Raccomandazioni di dimensionamento per disco VPS

Scegliere i valori in base alla dimensione del disco del VPS:

Dimensione disco VPS max-size max-file Max log per container Motivazione
25 GB 5m 3 15 MB Disco limitato; log minimi
50 GB 10m 5 50 MB VPS standard; ritenzione bilanciata
100 GB 25m 5 125 MB Disco generoso; ritenzione più lunga
200 GB+ 50m 5 250 MB Disco grande; debug esteso

Per un VPS da 50 GB (la scelta più comune), usare questa configurazione:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "5"
  }
}

Salvare il file e riavviare Docker:

sudo systemctl restart docker

Verificare che le nuove impostazioni predefinite siano attive:

docker info --format '{{.LoggingDriver}}'

Output atteso:

json-file

Confermare che le opzioni di log si applicano ai nuovi container avviandone uno e ispezionandolo:

docker run -d --name log-test alpine echo "test" && docker inspect --format '{{.HostConfig.LogConfig}}' log-test && docker rm log-test

Output atteso:

{json-file map[max-file:5 max-size:10m]}

Cosa succede ai container in esecuzione dopo la modifica di daemon.json?

I container esistenti mantengono la configurazione di log originale. Le nuove impostazioni si applicano solo ai container creati dopo il riavvio. I container in esecuzione devono essere ricreati per adottare i nuovi valori predefiniti.

Con Docker Compose:

docker compose down && docker compose up -d

Per i container standalone, fermarli e rimuoverli, poi riavviarli. Il flag --force-recreate funziona altrettanto bene:

docker compose up -d --force-recreate

Verificare che un container specifico usi la nuova configurazione:

docker inspect --format '{{.HostConfig.LogConfig}}' <container-name>

Output atteso:

{json-file map[max-file:5 max-size:10m]}

Come impostare i limiti di log in Docker Compose?

La configurazione di log per servizio in Docker Compose sovrascrive i valori predefiniti di daemon.json. Questo permette di assegnare limiti più stretti ai servizi verbosi e più spazio a quelli silenziosi.

Aggiungere il blocco logging sotto qualsiasi servizio:

services:
  web:
    image: nginx:alpine
    logging:
      driver: json-file
      options:
        max-size: "5m"
        max-file: "3"
    ports:
      - "80:80"

  api:
    image: node:22-alpine
    logging:
      driver: json-file
      options:
        max-size: "20m"
        max-file: "5"
    ports:
      - "3000:3000"

Per evitare di ripetere il blocco logging in ogni servizio, usare un'ancora YAML (YAML anchor):

x-logging: &default-logging
  logging:
    driver: json-file
    options:
      max-size: "10m"
      max-file: "5"

services:
  web:
    image: nginx:alpine
    <<: *default-logging
    ports:
      - "80:80"

  api:
    image: node:22-alpine
    <<: *default-logging
    ports:
      - "3000:3000"

  worker:
    image: myapp/worker:latest
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

Il servizio worker sovrascrive l'ancora con i propri limiti. Tutti gli altri servizi ottengono la configurazione condivisa.

Applicare la configurazione:

docker compose up -d --force-recreate

Controllo:

docker inspect --format '{{.HostConfig.LogConfig}}' web

Qual è la differenza tra i log driver json-file, local e journald?

Docker include tre log driver che salvano i log sull'host. Ognuno ha compromessi diversi. Il driver json-file è quello predefinito. Il driver local è la sostituzione raccomandata da Docker per l'efficienza disco. Il driver journald si integra con il journal di systemd.

Caratteristica json-file local journald
Rotazione predefinita Nessuna Sì (100 MB totali) Gestita da journald
Compressione Opzionale (compress: "true") Attiva per impostazione predefinita Gestita da journald
Supporto docker logs
Formato log JSON (leggibile) Binario (interno) Binario (journald)
Accesso da strumenti esterni Facile (file di testo) Non supportato Via journalctl
max-size predefinito Illimitato 20 MB per file Impostato in journald.conf
max-file predefinito 1 (nessuna rotazione) 5 file N/A

Quando usare ciascun driver

json-file è il valore predefinito sicuro. Funziona ovunque, supporta docker logs e i file di log sono JSON leggibili da qualsiasi strumento. Aggiungendo max-size e max-file funziona bene per la maggior parte dei VPS.

local è migliore per l'efficienza disco. La compressione è attiva per impostazione predefinita, la rotazione è integrata (5 file da 20 MB = 100 MB per container) e non serve configurare nulla. Il compromesso: i file di log usano un formato binario interno. Gli strumenti esterni che leggono i file direttamente (come Filebeat in modalità file) non possono analizzarli. Se i log vengono letti solo tramite docker logs o inviati tramite un plugin di logging Docker, conviene passare a local.

journald è la scelta giusta se si usa già il journal di systemd per tutti gli altri servizi e si vogliono i log dei container nello stesso posto. La rotazione è gestita dalla configurazione di journald (/etc/systemd/journald.conf). I log si leggono con journalctl invece di docker logs (anche se docker logs continua a funzionare).

Come passare al driver local

Modificare /etc/docker/daemon.json:

{
  "log-driver": "local",
  "log-opts": {
    "max-size": "10m",
    "max-file": "5"
  }
}

Riavviare Docker:

sudo systemctl restart docker

Controllo:

docker info --format '{{.LoggingDriver}}'

Output atteso:

local

Ricreare i container per applicare il nuovo driver:

docker compose up -d --force-recreate

Come usare il driver journald

Modificare /etc/docker/daemon.json:

{
  "log-driver": "journald"
}

Riavviare Docker:

sudo systemctl restart docker

Leggere i log di un container specifico:

sudo journalctl CONTAINER_NAME=mycontainer --no-pager -n 50

Seguire i log in tempo reale:

sudo journalctl CONTAINER_NAME=mycontainer -f

La rotazione di journald è controllata in /etc/systemd/journald.conf. Impostazioni principali:

[Journal]
SystemMaxUse=500M
SystemMaxFileSize=50M
MaxRetentionSec=7day

Dopo la modifica, riavviare journald:

sudo systemctl restart systemd-journald

Come automatizzare la pulizia Docker con cron o timer systemd?

La rotazione dei log impedisce ai singoli container di crescere senza limiti. Ma Docker accumula anche container fermati, immagini inutilizzate, build cache pendente e reti orfane. docker system prune pulisce tutto questo.

Cosa elimina effettivamente docker system prune?

Per impostazione predefinita, docker system prune rimuove:

  • Tutti i container fermati
  • Tutte le reti non utilizzate da alcun container in esecuzione
  • Tutte le immagini dangling (immagini senza tag non referenziate da alcun container)
  • Tutta la build cache inutilizzata

Non elimina:

  • I container in esecuzione
  • I volumi con nome (i dati del database sono al sicuro)
  • Le immagini con tag ancora referenziate
  • Le immagini utilizzate dai container in esecuzione

Il flag --all rimuove inoltre tutte le immagini inutilizzate (non solo quelle dangling). Il flag --volumes aggiunge i volumi anonimi alla pulizia. Usare --volumes con cautela: distrugge i dati nei volumi anonimi.

Opzione 1: job cron

Creare un job di pulizia settimanale:

sudo crontab -e

Aggiungere:

0 3 * * 0 /usr/bin/docker system prune -f >> /var/log/docker-prune.log 2>&1

Questo viene eseguito ogni domenica alle 03:00. Il flag -f salta la richiesta di conferma. L'output va in un file di log per la verifica.

Verificare che il crontab sia stato salvato:

sudo crontab -l

Opzione 2: timer systemd (consigliato)

I timer systemd sono più affidabili di cron. Scrivono nel journal, gestiscono le esecuzioni mancate (se il server era spento) e sono più facili da monitorare.

Creare la service unit:

sudo nano /etc/systemd/system/docker-prune.service
[Unit]
Description=Docker system prune
Wants=docker.service
After=docker.service

[Service]
Type=oneshot
ExecStart=/usr/bin/docker system prune -f --filter "until=168h"

Il flag --filter "until=168h" elimina solo gli oggetti più vecchi di 7 giorni. Questo protegge i container fermati di recente che potrebbe essere necessario ispezionare.

Creare il timer:

sudo nano /etc/systemd/system/docker-prune.timer
[Unit]
Description=Run Docker prune weekly

[Timer]
OnCalendar=Sun *-*-* 03:00:00
Persistent=true
RandomizedDelaySec=1800

[Install]
WantedBy=timers.target

Persistent=true significa che se il server era spento durante l'orario programmato, il job viene eseguito al prossimo avvio. RandomizedDelaySec distribuisce il carico se si hanno più server.

Abilitare e avviare il timer:

sudo systemctl daemon-reload
sudo systemctl enable --now docker-prune.timer

enable lo rende persistente ai riavvii. --now lo avvia immediatamente.

Verificare che il timer sia attivo:

sudo systemctl status docker-prune.timer

Controllare quando verrà eseguito:

sudo systemctl list-timers docker-prune.timer

Testarlo manualmente:

sudo systemctl start docker-prune.service

Controllare il risultato:

sudo journalctl -u docker-prune.service --no-pager -n 20

Come pulire i volumi Docker in sicurezza?

I volumi contengono dati persistenti: database, upload, configurazioni. Procedere con cautela.

Elencare tutti i volumi e il loro utilizzo:

docker volume ls

Mostrare solo i volumi non collegati ad alcun container:

docker volume ls -f dangling=true

Rimuovere i volumi dangling:

docker volume prune -f

Questo rimuove solo i volumi non attualmente utilizzati da alcun container (in esecuzione o fermato). I volumi con nome collegati a container fermati sono al sicuro.

Verificare cosa rimane:

docker volume ls

Non eseguire mai docker volume prune subito dopo docker system prune --volumes. Il system prune con --volumes gestisce già la pulizia dei volumi. Eseguirli entrambi è ridondante nel migliore dei casi.

Per rimuovere un volume specifico identificato come non necessario:

docker volume rm <volume-name>

Controllare sempre il contenuto di un volume prima di rimuoverlo:

docker volume inspect <volume-name>

Il campo Mountpoint mostra dove risiedono i dati su disco. Se ne può ispezionare il contenuto:

sudo ls -la $(docker volume inspect --format '{{.Mountpoint}}' <volume-name>)

Come monitorare l'uso del disco Docker su un VPS?

Il monitoraggio automatizzato previene le sorprese. Questa sezione configura un allarme a soglia che verifica l'uso del disco Docker e invia un avviso quando supera un limite.

Controllo manuale rapido

Eseguire questi due comandi per ottenere un'istantanea:

df -h /var/lib/docker
docker system df

Per un dettaglio per container e per immagine:

docker system df -v

Script di allarme automatico

Creare uno script di monitoraggio:

sudo nano /usr/local/bin/docker-disk-alert.sh
#!/bin/bash
# Alert when Docker's partition exceeds a usage threshold
THRESHOLD=80
MAILTO="admin@example.com"

USAGE=$(df /var/lib/docker | awk 'NR==2 {gsub(/%/,""); print $5}')

if [ "$USAGE" -ge "$THRESHOLD" ]; then
    DOCKER_DF=$(docker system df 2>&1)
    DISK_DF=$(df -h /var/lib/docker 2>&1)
    TOP_LOGS=$(find /var/lib/docker/containers/ -name "*-json.log" -exec ls -sh {} + 2>/dev/null | sort -rh | head -5)

    BODY="Docker disk usage on $(hostname) is at ${USAGE}%.

Disk usage:
${DISK_DF}

Docker breakdown:
${DOCKER_DF}

Largest log files:
${TOP_LOGS}"

    echo "$BODY" | mail -s "ALERT: Docker disk at ${USAGE}% on $(hostname)" "$MAILTO"
    logger -t docker-disk-alert "Docker disk usage at ${USAGE}% - alert sent"
fi

Impostare i permessi:

sudo chmod 750 /usr/local/bin/docker-disk-alert.sh

Verificare i permessi:

ls -la /usr/local/bin/docker-disk-alert.sh

Output atteso:

-rwxr-x--- 1 root root 612 Mar 19 12:00 /usr/local/bin/docker-disk-alert.sh

Lo script richiede mailutils (o mailx) per l'invio delle email. Installarlo se non presente:

sudo apt install -y mailutils

Testare lo script:

sudo /usr/local/bin/docker-disk-alert.sh

Se l'uso del disco è sotto la soglia, non succede nulla. Per testare il percorso di allarme, impostare temporaneamente THRESHOLD=1 nello script, eseguirlo, poi riportarlo al valore originale.

Pianificare l'allarme con un timer systemd

Creare il servizio:

sudo nano /etc/systemd/system/docker-disk-alert.service
[Unit]
Description=Check Docker disk usage

[Service]
Type=oneshot
ExecStart=/usr/local/bin/docker-disk-alert.sh

Creare il timer:

sudo nano /etc/systemd/system/docker-disk-alert.timer
[Unit]
Description=Check Docker disk usage every 6 hours

[Timer]
OnCalendar=*-*-* 00/6:00:00
Persistent=true

[Install]
WantedBy=timers.target

Abilitarlo:

sudo systemctl daemon-reload
sudo systemctl enable --now docker-disk-alert.timer

Controllo:

sudo systemctl list-timers docker-disk-alert.timer

Risoluzione dei problemi

Il disco è pieno e Docker non si avvia

Se Docker non si avvia perché il disco è completamente pieno:

sudo truncate -s 0 /var/lib/docker/containers/*/*-json.log
sudo systemctl start docker

Poi configurare immediatamente la rotazione dei log come descritto sopra.

Un errore di sintassi in daemon.json impedisce l'avvio di Docker

Docker non si avvia se daemon.json contiene JSON non valido. Validare il file:

sudo python3 -m json.tool /etc/docker/daemon.json

Se viene stampato il JSON formattato, la sintassi è valida. Se viene stampato un errore, correggere la riga indicata.

Controllare il messaggio di errore di Docker:

sudo journalctl -u docker.service -n 20 --no-pager

I log continuano a crescere dopo la configurazione della rotazione

La rotazione si applica solo ai nuovi container. I container esistenti mantengono la configurazione originale. Ricrearli:

docker compose up -d --force-recreate

Verificare che la nuova configurazione sia attiva:

docker inspect --format '{{.HostConfig.LogConfig}}' <container-name>

docker system prune non ha liberato molto spazio

docker system prune non tocca i container in esecuzione, i loro log né i volumi con nome. Se il problema di spazio riguarda specificamente i log, troncare i file di log o configurare la rotazione. Se riguarda i volumi, usare docker volume prune dopo aver verificato che nessun dato necessario verrà perso.

Controllare cosa sta consumando spazio:

sudo du -sh /var/lib/docker/*

Questo mostra l'utilizzo per sottosistema Docker: containers (log), overlay2 (immagini/layer), volumes e altri.

Una configurazione completa di gestione dei log Docker su un VPS ha quattro livelli:

  1. Rotazione globale in /etc/docker/daemon.json con max-size e max-file previene la crescita illimitata dei log per tutti i container.
  2. Override per servizio in Docker Compose assegnano limiti più stretti ai servizi verbosi.
  3. Pulizia automatica con docker system prune tramite un timer systemd rimuove container morti, immagini inutilizzate e build cache.
  4. Monitoraggio disco con uno script di allarme intercetta i problemi prima che diventino interruzioni di servizio.

Dopo aver completato la configurazione, verificarne il funzionamento: controllare docker inspect sui container, eseguire docker system df per confermare l'uso corrente e attendere che il timer di pulizia venga eseguito almeno una volta. Controllare journalctl -u docker-prune.service per confermare l'esecuzione.


Copyright 2026 Virtua.Cloud. Tutti i diritti riservati. Questo contenuto è un'opera originale del team Virtua.Cloud. La riproduzione, ripubblicazione o redistribuzione senza autorizzazione scritta è vietata.

Pronto a provare?

Mantieni il tuo VPS Docker pulito con la rotazione dei log.

Vedi piani VPS
Rotazione log Docker: gestione disco VPS