Introduzione
Il caso più fastidioso non è il container che crasha subito. È quello che risulta healthy, espone la porta, ma l’applicazione non serve traffico o perde dati dopo un restart.
Succede spesso con Docker Compose quando si combinano bind mount, limiti di memoria stretti, healthcheck troppo ottimistici e una rete interna configurata male. Il sintomo inganna. La dashboard dice verde. Gli utenti vedono errori, timeout o contenuti vecchi.
In questo articolo lavoriamo sullo scenario peggiore: cosa fare quando va storto. Prima diagnosi, poi rollback, poi ripristino. L’obiettivo è evitare il classico deploy che “sembra riuscito” e lascia il servizio in uno stato ambiguo.
Prerequisiti
- Docker Engine recente e Docker Compose v2.
- Accesso alla macchina host con privilegi sudo.
- Un file compose.yaml versionato in Git.
- Log dell’applicazione e del container disponibili.
- Un backup o almeno uno snapshot del volume dati.
Note: i comandi sotto usano esempi generici. Adatta nomi di servizio, percorsi e porte al tuo stack reale.
Warning: se il volume contiene dati critici, non cancellarlo prima di aver verificato che il ripristino sia possibile. Il problema più comune è fare pulizia troppo presto.
Step 1: capire se il guasto è nel processo, nella rete o nel volume
Il primo errore è guardare solo lo stato del container. Un container può essere running e non funzionare per tre motivi diversi: il processo applicativo è bloccato, la rete interna non risolve il servizio, oppure il volume montato ha dati incoerenti.
Parti da tre controlli in parallelo. Stato, log e mount. Non saltare nessuno dei tre.
docker compose ps
docker compose logs --tail=100 web
docker inspect web --format '{{json .Mounts}}' | jq
# Output:
web deve risultare Up. Nei log devi vedere l’avvio completo o un errore preciso. Nei mount devi verificare che il percorso host punti al volume giusto.
Se il log mostra messaggi come permission denied o read-only file system, il problema è quasi sempre nel mount. Se vedi connection refused verso database o cache, il problema è nella rete o nel servizio dipendente.
Step 2: verificare la rete Compose quando il DNS interno mente
Molti stack Compose falliscono dopo un riavvio perché il servizio usa un nome host interno cambiato o perché la rete è stata ricreata. Il container risulta sano, ma l’app non raggiunge il database o il broker.
La diagnosi migliore è entrare nel container e risolvere il nome dal suo namespace di rete. Non fidarti della risoluzione dall’host.
docker compose exec web sh -lc 'getent hosts db && nc -zv db 5432'
# Output:
Devi vedere un IP interno della rete Compose e una connessione riuscita sulla porta del servizio.
Se il nome non si risolve, controlla che entrambi i servizi siano sulla stessa rete. Se usi reti multiple, il servizio potrebbe essere collegato a quella sbagliata.
services:
web:
networks:
- front
- back
db:
networks:
- back
networks:
front:
back:
Note: se il problema compare dopo down e up, verifica che nessun script esterno si aspetti un IP fisso. Con Compose, il nome servizio è la regola corretta. L’IP non lo è.
Step 3: controllare i volumi quando i dati “spariscono” dopo il restart
Il sintomo classico è questo: l’app parte, ma sembra vuota. Utenti disconnessi, cache azzerata, file mancanti, configurazione resettata. Spesso il volume non è sparito. È stato montato nel posto sbagliato.
Con un bind mount basta un errore di percorso per creare una directory nuova e vuota sull’host. Il container la usa senza protestare. Il risultato è una perdita apparente di dati.
docker inspect web --format '{{range .Mounts}}{{println .Type .Source "->" .Destination}}{{end}}'
ls -lah /srv/app/data
# Output:
Il percorso host deve contenere i file attesi. Se trovi directory appena create, probabilmente stai montando il path sbagliato o l’utente del container non ha permessi coerenti.
Per un ripristino rapido, ferma il servizio e rimonta il volume corretto nel compose. Poi riavvia solo il servizio interessato, non l’intero stack.
services:
web:
volumes:
- type: bind
source: /srv/app/data
target: /var/lib/app
consistency: delegated
Warning: non usare un bind mount temporaneo “di emergenza” senza documentarlo. È un classico rollback incompleto che torna a colpire al prossimo deploy.
Step 4: leggere i limiti di risorse prima che il kernel tagli il processo
Un container con memoria troppo stretta può passare il healthcheck e morire pochi minuti dopo. Oppure restare vivo ma degradato. L’errore più comune è interpretare exit code 137 come bug applicativo. Spesso è il kernel che ha ucciso il processo per OOM.
Verifica i limiti effettivi, non solo quelli dichiarati nel file Compose.
docker inspect web --format 'Memory={{.HostConfig.Memory}} CpuQuota={{.HostConfig.CpuQuota}} PidsLimit={{.HostConfig.PidsLimit}}'
docker stats --no-stream web
journalctl -k --since '30 min ago' | grep -i oom
# Output:
Devi vedere i limiti impostati e, se c’è stato un OOM, una riga del kernel che conferma l’uccisione del processo.
Se il servizio usa PHP-FPM, Node.js o Java, il problema emerge spesso sotto carico e non in avvio. Il healthcheck resta verde perché controlla una pagina leggera o un endpoint cache-friendly.
services:
web:
mem_limit: 512m
cpus: 1.00
pids_limit: 200
Note: non alzare subito tutti i limiti. Prima misura il picco reale con docker stats e con i log dell’app. Un aumento cieco nasconde il problema.
Step 5: rendere il healthcheck davvero utile
Il healthcheck deve misurare il servizio, non solo il processo. Un curl localhost che riceve una pagina statica non basta se il backend non è raggiungibile o se il database è in errore.
Un buon healthcheck prova il percorso critico dell’app. Per esempio, un endpoint che verifica DB, cache e scrittura minima su disco.
services:
web:
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:8080/healthz || exit 1"]
interval: 10s
timeout: 3s
retries: 5
start_period: 30s
# Output:
Il container deve passare a healthy solo quando il servizio risponde davvero. Se l’endpoint è troppo leggero, il risultato è un falso positivo.
Se il problema compare dopo il deploy, confronta il vecchio e il nuovo endpoint. A volte il nuovo codice non espone più la stessa verifica, ma Compose continua a considerarla valida.
Quando il healthcheck crea un loop
Un healthcheck troppo aggressivo può uccidere un container ancora in warm-up. Se l’app impiega 40 secondi ad aprire il pool DB, ma il start_period è di 10 secondi, avrai un falso fallimento ad ogni riavvio.
In quel caso aumenta il start_period e riduci la frequenza delle prove. La regola è semplice: il check non deve competere con l’avvio.
Step 6: fare rollback senza distruggere i dati
Quando il deploy rompe l’ambiente, la priorità è tornare a una versione funzionante senza toccare il volume dati. Il rollback corretto riguarda immagine, configurazione e rete. Non il contenuto persistente, salvo corruzione verificata.
Se usi Git, conserva sempre una release precedente pronta. Con Compose, il rollback più pulito è ripristinare il file e rilanciare solo i servizi interessati.
git checkout HEAD~1 -- compose.yaml
docker compose pull web
docker compose up -d --no-deps web
# Output:
Il servizio deve ripartire con la configurazione precedente. I dati nel volume restano intatti.
Se hai già aggiornato un volume schema-sensitive, fai attenzione. Tornare a un’immagine vecchia con dati nuovi può creare incompatibilità. In quel caso serve un dump, non solo un rollback.
Note: se il deploy è stato fatto con tag latest, il rollback è più fragile. Meglio versioni immutabili, come app:2026.03.25-1.
Step 7: ripristinare i dati quando il volume è corrotto
La corruzione vera è meno frequente del mount sbagliato, ma va trattata subito. Se il database embedded, la cache persistente o una directory di upload mostrano file incoerenti, fermati e copia il volume prima di tutto.
Lavora su una copia, non sull’originale.
docker compose stop web
rsync -aHAX /srv/app/data/ /srv/app/data.bak/
# Output:
Devi ottenere una copia completa, pronta per un tentativo di ripristino o per un confronto file per file.
Se il servizio è un database, usa lo strumento nativo di recovery. Se è una directory di file, confronta checksum e timestamp. Se è una cache, spesso conviene svuotarla e ricostruirla, non ripararla.
Verifica finale
Dopo il rollback o il ripristino, esegui sempre la stessa sequenza. Non affidarti alla sola dashboard.
- Il container deve risultare Up e healthy.
- La rete interna deve risolvere i servizi dipendenti.
- I mount devono puntare al path corretto.
- I limiti risorse devono essere coerenti con il carico reale.
- I log non devono mostrare retry infiniti o errori di permesso.
docker compose ps
docker compose exec web sh -lc 'curl -fsS http://localhost:8080/healthz && getent hosts db'
docker stats --no-stream web
# Output:
Lo stato deve essere stabile per almeno alcuni minuti. Se il servizio degrada subito dopo il check, il problema è ancora aperto.
Troubleshooting
Errore: service "db" is unhealthy
Cause: il healthcheck dipende da un servizio upstream non ancora pronto o bloccato.
Fix: aumenta start_period e verifica il servizio dipendente con un check diretto.
docker compose exec db sh -lc 'pg_isready -U app'
# Output:
accepting connections
Errore: permission denied while trying to connect to the Docker daemon socket
Cause: l’utente corrente non ha permessi sul socket Docker.
Fix: usa sudo oppure aggiungi l’utente al gruppo docker e riapri la sessione.
sudo usermod -aG docker $USER
newgrp docker
# Output:
Il comando docker ps deve funzionare senza sudo nella nuova sessione.
Errore: bind: address already in use
Cause: la porta host è già occupata da un vecchio container, da un processo locale o da un rollback incompleto.
Fix: trova il processo, libera la porta e rilancia solo il servizio necessario.
ss -ltnp | grep ':8080'
docker compose down
# Output:
La porta deve risultare libera prima del nuovo up -d.
Conclusione
Quando un Compose stack va storto, il problema raramente è solo “Docker”. Di solito c’è una combinazione di rete, volume e limiti risorse, con un healthcheck troppo fiducioso.
La disciplina giusta è sempre la stessa: diagnosticare con dati reali, fare rollback senza toccare i dati persistenti e ripristinare solo dopo aver capito la causa.
Prossimo passo concreto: prendi il tuo compose.yaml, aggiungi un healthcheck che tocchi una dipendenza reale e prepara un rollback con tag versionati, non con latest.
Commenti (0)
Nessun commento ancora.
Segnala contenuto
Elimina commento
Eliminare definitivamente questo commento?
L'azione non si può annullare.