Installare Docker e Docker Compose su Debian e Ubuntu

Pubblicato il 17 min di lettura

Perché docker.io su Debian 12 è troppo vecchio mentre su Ubuntu va benissimo, come integrare il repository ufficiale con un keyring invece che con apt-key, perché Compose è un plugin e perché il gruppo docker equivale di fatto a root.

Installare Docker richiede cinque minuti. Installarlo in modo che un anno dopo il server riceva ancora gli aggiornamenti di sicurezza, che il disco di sistema non si riempia e che non finisca per sbaglio ogni utente in giro con i privilegi di root richiede un po' più di tempo. Questo articolo tratta la seconda variante, verificata su Debian 13 (Trixie), Debian 12 (Bookworm), Ubuntu 24.04 (Noble) e Ubuntu 22.04 (Jammy).

docker.io oppure Docker CE: la differenza che quasi nessuno spiega onestamente

Ci sono due strade per arrivare a Docker. Il pacchetto docker.io proviene dai repository della distribuzione e viene compilato e mantenuto da Debian o da Ubuntu. Il pacchetto docker-ce proviene dal repository di Docker stessa. Entrambi contengono lo stesso software, ma in versioni di età molto diversa.

Queste sono le versioni attualmente presenti nei repository delle distribuzioni (misurate con apt-cache policy in container appena creati):

Sistemadocker.io nei repository della distribuzione
Debian 13 (Trixie)26.1.5
Debian 12 (Bookworm)20.10.24
Ubuntu 24.04 (Noble)29.1.3
Ubuntu 22.04 (Jammy)29.1.3

Questo è il punto vero della questione, e quasi tutte le guide lo riducono a una raccomandazione generica. La realtà è più sfumata:

  • Ubuntu 24.04 e 22.04: docker.io è alla 29.1.3 e quindi praticamente al livello della versione upstream attuale. Se non hai esigenze particolari, qui puoi prendere il pacchetto della distribuzione senza rimorsi. Gli aggiornamenti di sicurezza arrivano poi attraverso il normale canale Ubuntu.
  • Debian 13: la 26.1.5 è utilizzabile, ma resta un bel pezzo indietro rispetto a upstream. Per la maggior parte dei casi d'uso basta.
  • Debian 12: la 20.10.24 è il caso problematico. Questo ramo è arrivato a fine ciclo di vita upstream ormai da anni. Debian integra sì le patch di sicurezza, ma molte funzioni moderne mancano semplicemente, tra cui una versione recente di BuildKit e parecchia compatibilità con Compose.

A questo si aggiunge una differenza pratica: docker-ce porta con sé Buildx e Compose come pacchetti plugin separati, allineati alla Engine. Con docker.io devi invece recuperarti questi pezzi per conto tuo da docker-buildx e docker-compose-v2, e docker-compose-v2 si trova esclusivamente nei repository Ubuntu, mentre su Debian il pacchetto non esiste affatto.

Quello che non funziona: usare i due in parallelo. Il pacchetto containerd.io del repository Docker è in conflitto con il pacchetto containerd della distribuzione. Devi scegliere.

Regola pratica: su Ubuntu docker.io è una scelta legittima. Su Debian 12 no. Per i server che fanno girare stack Compose con sintassi recente, Docker CE è ovunque la decisione migliore.

Rimuovere i vecchi pacchetti prima che succeda qualsiasi altra cosa

Se c'è già un Docker in qualche forma, deve sparire, altrimenti l'installazione fallisce per conflitti tra pacchetti. Il ciclo seguente rimuove tutti i soliti sospetti e intercetta i pacchetti che non sono installati o che nella tua distribuzione non esistono proprio:

for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove -y $pkg || true; done

Il || true nel corpo del ciclo non è un vezzo estetico, è necessario. Su Debian apt-get si interrompe alla voce docker-compose-v2 con E: Unable to locate package docker-compose-v2 e codice di uscita 100, perché questo pacchetto esiste solo nei repository Ubuntu, non in bookworm né in trixie (e nemmeno in bullseye, dove manca anche podman-docker). Il ciclo prosegue, ma lascia dietro di sé un codice di uscita diverso da 0, ed è esattamente lì che muore uno script con set -e oppure una catena con &&. I messaggi del tipo "Unable to locate package" a questo punto sono quindi normali e possono essere ignorati, come indica anche la documentazione ufficiale di Docker.

Importante da sapere: così non si perde nulla. Le tue immagini, i container e i volumi si trovano in /var/lib/docker, e questa directory apt-get remove non la tocca. Dopo l'installazione di Docker CE i tuoi container sono di nuovo lì. Solo sudo rm -rf /var/lib/docker cancella davvero, e quello è irreversibile.

Archiviare la chiave nel modo giusto: apt-key è storia

Molte guide in rete contengono ancora questa riga:

curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -

Su nessuno dei quattro sistemi trattati qui questo funziona più in modo sensato. apt-key è deprecato e su Debian 13 non è nemmeno più presente. Il motivo non è estetico: una chiave in /etc/apt/trusted.gpg firma tutti i repository, non solo quello per cui era pensata. Un mirror compromesso potrebbe così rifilarti pacchetti qualsiasi.

La forma corretta è un portachiavi dedicato sotto /etc/apt/keyrings/, associato tramite Signed-By a una sola sorgente.

sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings

Il comando seguente scarica la chiave giusta e funziona sia su Debian sia su Ubuntu, perché legge l'ID della distribuzione da /etc/os-release:

sudo curl -fsSL "https://download.docker.com/linux/$(. /etc/os-release && echo "$ID")/gpg" -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

E adesso il passaggio che praticamente nessuno descrive: verifica l'impronta digitale prima di affidare il tuo sistema a quella chiave.

gpg --show-keys /etc/apt/keyrings/docker.asc

L'output deve contenere l'impronta 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88 e l'identificativo Docker Release (CE deb) <docker@docker.com>. Se non corrisponde, interrompi. In quel caso c'è qualcosa che non va nella tua connessione oppure nella sorgente.

Installare Docker CE, con uno snippet valido per tutti e quattro i sistemi

La documentazione ufficiale mostra blocchi separati per Debian e per Ubuntu. È superfluo. Il blocco seguente scrive il repository nel moderno formato deb822 e determina da sé distribuzione, nome in codice e architettura:

sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/$(. /etc/os-release && echo "$ID")
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

Due dettagli che fanno risparmiare tempo. Primo, ${UBUNTU_CODENAME:-$VERSION_CODENAME}: sulle derivate di Ubuntu come Linux Mint in VERSION_CODENAME compare il nome della derivata, non quello di Ubuntu. Secondo, la riga Architectures: senza di essa apt protesta sui sistemi con l'architettura estranea i386 attivata, con un lungo avviso su elenchi di pacchetti inesistenti.

Controlla il risultato prima di andare avanti:

cat /etc/apt/sources.list.d/docker.sources

In Suites deve comparire trixie, bookworm, noble oppure jammy. Se c'è scritto altro, il passaggio successivo finisce in errore. Poi seguono aggiornamento e installazione:

sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

I cinque pacchetti sono: il daemon, lo strumento da riga di comando, il runtime dei container, il builder moderno per le immagini e Compose.

Da cosa capisci che funziona davvero

Il fatto che apt-get sia arrivato in fondo senza errori dice soltanto che ci sono dei file sul disco. Le quattro verifiche seguenti mostrano se il sistema lavora davvero.

Primo: il client raggiunge il daemon?

docker version

Non conta la sezione Client, conta che sotto compaia una sezione Server: Docker Engine - Community con un numero di versione. Se manca, il daemon non è in esecuzione oppure non hai il permesso di accedere al socket.

Secondo: quale driver di storage è attivo?

docker info --format '{{.Driver}}'
docker info --format '{{.CgroupVersion}}'

Qui si nasconde una novità a cui molte guide più vecchie danno la risposta sbagliata. Da Docker Engine 29, nelle nuove installazioni, è preimpostato l'image store di containerd. Il driver si chiama allora overlayfs e non più overlay2. Entrambi i valori vanno bene. Quello che non vuoi vedere è vfs: questo driver di emergenza copia per intero ogni layer, divora un multiplo dello spazio su disco ed è di una lentezza esasperante. Compare tipicamente quando Docker gira in un ambiente privo del supporto kernel adeguato. La versione di cgroup deve dare 2 su tutti e quattro i sistemi.

Se hai aggiornato un sistema esistente e all'improvviso sembra che tutte le immagini siano sparite: non sono state cancellate. Al cambio di image store il contenuto dell'altro archivio viene solo nascosto e ricompare non appena torni indietro. Il ritorno passa da /etc/docker/daemon.json:

{
  "features": {
    "containerd-snapshotter": false
  }
}

Terzo: parte davvero un container?

docker run --rm hello-world

Quarto: rete e risoluzione dei nomi funzionano dentro il container? Questo test manca in quasi tutte le guide, benché sia proprio qui che nasce la maggior parte dei problemi successivi:

docker run --rm alpine:3 ping -c 2 1.1.1.1
docker run --rm alpine:3 nslookup deb.debian.org

Se il ping risponde ma la risoluzione dei nomi fallisce, di solito la causa è un server DNS in ascolto solo su 127.0.0.53. Dal container quell'indirizzo non è raggiungibile. Si rimedia con una voce in /etc/docker/daemon.json contenente "dns": ["9.9.9.9"] e un riavvio del daemon.

Compose è un plugin, non più un programma a sé

Il vecchio docker-compose con il trattino era un programma Python separato. È stato dismesso e non viene più distribuito. Il successore è un plugin scritto in Go che si richiama come sottocomando della CLI di Docker, quindi docker compose con lo spazio.

docker compose version

Una nota sul numero di versione, perché genera confusione con regolarità: l'espressione "Compose V2" indica la riscrittura in Go, non il numero di versione. L'output mostra oggi una versione del ramo 5. È corretto e non si tratta di un prodotto diverso.

Nel passaggio saltano all'occhio due cose. Da un lato la chiave version: all'inizio di docker-compose.yml è diventata superflua e produce un avviso:

WARN[0000] docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion

Cancella semplicemente quella riga. Dall'altro cambia la denominazione: Compose ricava il nome del progetto dal nome della directory e crea i container con il trattino al posto del trattino basso, quindi mioprogetto-web-1 invece di mioprogetto_web_1. Gli script che si rivolgono ai container con nomi fissi si rompono per questo motivo. In casi del genere imposta il nome del progetto in modo esplicito tramite name: nel file Compose oppure con -p.

Se vuoi restare consapevolmente sui pacchetti della distribuzione, il pacchetto giusto si chiama docker-compose-v2 e fornisce lo stesso sottocomando. È però disponibile solo nei repository Ubuntu. Verifica prima la versione disponibile:

apt-cache policy docker-compose-v2

Su Ubuntu 24.04 e 22.04 compare una tabella con la versione installata e quella candidata. Su Debian 13 e Debian 12 il comando non stampa proprio nulla, un output vuoto con codice di uscita 0. Non è un errore, è la risposta: su Debian questo pacchetto non esiste, e lì la strada verso Compose passa da docker-compose-plugin del repository Docker.

Il gruppo docker è root, solo per una via traversa

Perché un utente normale possa usare Docker senza sudo, di solito lo si aggiunge al gruppo docker:

sudo groupadd -f docker
sudo usermod -aG docker $USER

L'appartenenza al gruppo diventa effettiva solo con un nuovo accesso. Puoi verificarla dopo aver rifatto il login con id -nG. Se non vuoi rifare l'accesso, con newgrp docker avvii una shell che ha già il nuovo gruppo.

E adesso la parte che devi aver capito: l'appartenenza al gruppo docker equivale ai privilegi di root sull'intero server. Non è una valutazione teorica, è una conseguenza diretta del funzionamento. Chi può parlare con il socket di Docker può impartire al daemon (che gira come root) qualsiasi ordine. Basta un solo comando:

docker run -it -v /:/hostfs alpine:3 chroot /hostfs sh

Il risultato è una shell di root sul sistema host, senza sudo, senza richiesta di password, senza traccia nel log di sudo. Leggere /etc/shadow, depositare chiavi SSH, sostituire servizi: tutto è possibile. La documentazione di Docker lo formula in modo breve e inequivocabile: "The docker group grants root-level privileges to the user."

Conseguenze pratiche per un server esposto su Internet:

  • Aggiungi al gruppo esclusivamente account a cui affideresti comunque i privilegi di root.
  • L'utente sotto cui gira un'applicazione web o un runner CI non ne fa parte. Un'intrusione nell'applicazione diventerebbe altrimenti in automatico un'intrusione nel server.
  • Se ti serve tracciabilità, rinuncia al gruppo e richiama Docker con sudo docker. Almeno così la chiamata compare nel log.
  • Per una separazione vera esiste la modalità rootless. Si configura con il pacchetto docker-ce-rootless-extras e con lo strumento dockerd-rootless-setuptool.sh install e richiede in più uidmap. Il prezzo: le porte sotto la 1024 non si possono occupare senza configurazione aggiuntiva, e alcune funzioni di rete si comportano diversamente.

Avvio automatico: la trappola si chiama docker.socket

Il comando standard è noto:

sudo systemctl enable --now docker.service
sudo systemctl enable --now containerd.service

Meno noto è il motivo per cui la disattivazione spesso non ha effetto. Oltre a docker.service, Docker porta con sé anche un docker.socket. Questa unit resta in ascolto sul socket e avvia il daemon in automatico al primo accesso. Chi quindi esegue systemctl disable docker.service e poi constata che Docker gira lo stesso non ha visto un fantasma: il primo docker ps ha rialzato il daemon attraverso la unit del socket. Per uno spegnimento completo servono entrambe:

sudo systemctl disable --now docker.service docker.socket

Lo stato si verifica con systemctl is-enabled docker.service, che deve restituire enabled.

Il secondo punto riguarda i tuoi container. Se un container torna su dopo un riavvio non lo decide systemd, lo decide la restart policy. E qui c'è una differenza che sorprende con regolarità: always riavvia un container anche quando lo avevi fermato di proposito prima del riavvio. unless-stopped rispetta il tuo stop manuale anche attraverso il riavvio. Per i server unless-stopped è di norma la scelta giusta:

services:
  web:
    image: nginx:stable
    restart: unless-stopped

Il test per questo non è docker ps, ma un vero riavvio del server con controllo successivo.

Rotazione dei log: il motivo più frequente di un disco di sistema pieno

Docker scrive l'output di ogni container, per impostazione predefinita, in un file JSON sotto /var/lib/docker/containers/. Questo file cresce di default senza limiti. Un reverse proxy loquace può così accumulare nel giro di mesi decine di gigabyte, finché il server non si pianta con no space left on device. Il colpevole a quel punto è difficile da trovare, perché du nella directory dell'applicazione non mostra nulla di anomalo.

La soluzione va messa su ogni server, e va messa prima che si presenti il primo problema:

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}
EOF

Se esiste già un daemon.json, questo comando lo sovrascrive. Guarda prima cosa contiene e inserisci le chiavi a mano se serve. Dopodiché:

sudo systemctl restart docker

Tre punti su cui si sbaglia comunque:

  • I valori devono essere stringhe tra virgolette. "max-file": 3 senza virgolette impedisce al daemon di ripartire.
  • L'impostazione vale solo per i container creati ex novo. Quelli esistenti mantengono la vecchia configurazione finché non vengono ricreati, con Compose quindi tramite docker compose up -d --force-recreate.
  • Cancellare con rm un file di log traboccato non restituisce spazio su disco, perché il daemon tiene ancora il file aperto. Usa invece sudo truncate -s 0 <percorso>.

Se l'impostazione ha effetto su un container concreto lo verifichi così:

docker inspect --format '{{json .HostConfig.LogConfig}}' miocontainer

Fare pulizia con docker system prune, senza perdere dati

Immagini inutilizzate, build interrotte e la cache di BuildKit si sommano. Prima di tutto guarda dove finisce lo spazio:

docker system df
docker system df -v

Il comando standard per la pulizia rimuove i container fermi, le reti inutilizzate, le immagini senza nome e la cache di build:

docker system prune

Due opzioni meritano rispetto. -a cancella in più tutte le immagini che in quel momento non sono usate da nessun container, quindi anche immagini di base curate con attenzione. Su un server con connettività ridotta il download successivo può richiedere parecchio tempo. Nettamente più pericolosa è --volumes: questa opzione elimina i volumi con nome che non hanno un container associato. Se il tuo container di database è appena stato cancellato ma il volume contiene ancora i dati, dopo quei dati non ci sono più. Non esiste un cestino.

Non usare mai --volumes in un job di pulizia automatico.

Ha senso invece un periodo di grazia temporale, così sparisce solo il materiale davvero vecchio:

docker system prune -a --filter "until=168h"
docker builder prune --filter "until=168h"

Come job settimanale, di notte per non pesare sul sistema e senza cancellazione dei volumi, basta una riga in /etc/cron.d/docker-prune:

15 4 * * 0 root /usr/bin/docker system prune -af --filter "until=168h" > /dev/null 2>&1

La prova del successo è un nuovo docker system df con la colonna "RECLAIMABLE" calata.

I messaggi di errore parola per parola e cosa c'è dietro

permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock
L'utente non è nel gruppo docker, oppure l'appartenenza al gruppo non è ancora effettiva nella sessione corrente. Rifai l'accesso oppure usa newgrp docker.

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
Il daemon non è in esecuzione. Causa e testo esatto li fornisce systemctl status docker e, in modo più dettagliato, journalctl -u docker -n 50 --no-pager. Molto spesso dietro c'è un /etc/docker/daemon.json difettoso. Quel file deve essere JSON valido, basta una sola virgola di troppo.

docker: 'compose' is not a docker command.
Manca il plugin. Installa docker-compose-plugin dal repository Docker oppure, solo su Ubuntu, docker-compose-v2 della distribuzione.

E: Conflicting values set for option Signed-By regarding source https://download.docker.com/linux/debian/ trixie: /etc/apt/keyrings/docker.asc != /etc/apt/keyrings/docker.gpg
Il classico dopo aver seguito più guide di fila: esistono contemporaneamente un vecchio /etc/apt/sources.list.d/docker.list e il nuovo docker.sources. Cancella il file vecchio e ripeti sudo apt-get update. Fatti un quadro d'insieme con ls -l /etc/apt/sources.list.d/.

The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 8D81803C0EBFCD88
La chiave non si trova dove Signed-By se la aspetta, oppure il file scaricato è incompleto (per esempio perché un proxy ha restituito una pagina di errore HTML). gpg --show-keys /etc/apt/keyrings/docker.asc mostra subito se dentro c'è davvero una chiave.

E: The repository 'https://download.docker.com/linux/debian trixie Release' does not have a Release file.
Il nome in codice non corrisponde alla sorgente. Succede sulle distribuzioni derivate e quando si mescolano guide per Debian e guide per Ubuntu. Controlla le righe URIs e Suites in docker.sources.

Bind for 0.0.0.0:80 failed: port is already allocated
Un altro servizio occupa la porta, spesso un server web installato direttamente sull'host. sudo ss -tulpn | grep :80 ne indica il responsabile.

Docker e il firewall: una parola sulla sicurezza

Una particolarità che su un server raggiungibile pubblicamente può costare caro: Docker inserisce le proprie regole di inoltro nella tabella NAT e aggira così le regole che hai curato in ufw. Un container avviato con -p 5432:5432 è raggiungibile da Internet anche se ufw status non permette quella porta da nessuna parte. Questo vale tuttora, benché Docker Engine 28 abbia irrigidito nel complesso il comportamento di rete e impedito dall'esterno l'accesso alle porte non pubblicate.

La contromisura più semplice e affidabile è legare esplicitamente all'indirizzo di loopback i servizi che servono solo in locale:

services:
  db:
    image: postgres:17
    ports:
      - "127.0.0.1:5432:5432"
    restart: unless-stopped

Meglio ancora: non pubblicare affatto queste porte e lascia che i container comunichino tra loro attraverso una rete Docker comune. Il risultato si verifica al meglio da un secondo computer, perché una prova fatta dal server stesso non risponde alla domanda decisiva.

In breve

Su Debian 12 prendi in ogni caso Docker CE, su Ubuntu puoi scegliere tra docker.io e Docker CE. Aggiungi il repository con un portachiavi dedicato e un'impronta verificata, non con apt-key. Usa docker compose con lo spazio. Tratta il gruppo docker come un permesso di root, perché è esattamente questo. E configura la rotazione dei log e un job di prune settimanale prima che il server si pianti per la prima volta con il disco pieno.

Sull'argomento: Configurare il firewall UFW su Debian e Ubuntu e Installare Nginx su Debian e Ubuntu.

Domande frequenti

Devo installare docker.io oppure docker-ce?
Su Debian 12 senza dubbio Docker CE, perché lì il pacchetto della distribuzione è fermo alla 20.10.24 e quel ramo è arrivato a fine vita upstream. Su Debian 13 docker.io fornisce la 26.1.5, su Ubuntu 24.04 e 22.04 addirittura la 29.1.3, quindi praticamente lo stato attuale. Lì il pacchetto della distribuzione è una scelta legittima, a patto che tu installi Buildx e Compose separatamente. Il pacchetto docker-compose-v2 esiste però solo nei repository Ubuntu, su Debian la strada verso Compose passa da docker-compose-plugin del repository Docker.
Perché docker-compose con il trattino non funziona più?
Il vecchio docker-compose era un programma Python autonomo e non viene più distribuito. Il successore è un plugin in Go della CLI di Docker e si richiama come docker compose con lo spazio. Si trova nel pacchetto docker-compose-plugin (repository Docker) oppure, solo su Ubuntu, in docker-compose-v2 della distribuzione. Verifica con: docker compose version.
Il gruppo docker è davvero pericoloso come si dice?
Sì. Chi può accedere al socket di Docker è in grado di impartire qualsiasi ordine al daemon, che gira come root: per esempio montare la radice del filesystem dell'host dentro un container e aprirci una shell di root. La documentazione di Docker parla di root-level privileges. Aggiungi soltanto account a cui affideresti comunque i privilegi di root, oppure passa alla modalità rootless.
Perché docker info mi mostra overlayfs invece di overlay2?
Da Docker Engine 29, nelle nuove installazioni, è preimpostato l'image store di containerd. Il suo snapshotter si chiama overlayfs. È corretto e non è un errore. Problematico sarebbe solo il valore vfs, che indica un supporto kernel mancante e consuma moltissimo spazio su disco.
Le mie immagini sono sparite dopo un aggiornamento, sono state cancellate?
Di norma no. Nel passaggio tra image store classico e image store di containerd il contenuto dell'altro archivio viene soltanto nascosto, i dati restano sul disco. Torna indietro per prova tramite features containerd-snapshotter in /etc/docker/daemon.json e le immagini ricompaiono.
Come evito che i log dei container riempiano il disco?
Il driver json-file per impostazione predefinita non ruota. Inserisci in /etc/docker/daemon.json un blocco log-opts con max-size 10m e max-file 3, i valori devono essere stringhe tra virgolette. Dopo systemctl restart docker la regola vale solo per i container creati ex novo, quelli esistenti vanno ricreati con docker compose up -d --force-recreate.
docker system prune è privo di rischi?
Senza opzioni in larga misura sì, rimuove i container fermi, le reti inutilizzate, le immagini senza nome e la cache di build. L'opzione -a cancella in più tutte le immagini che in quel momento non sono in uso. Pericolosa è --volumes, perché fa sparire i volumi con nome privi di container associato, quindi eventualmente il tuo database. Nei job automatici --volumes non ci deve mai finire.

Docker Docker Compose Debian Ubuntu Server Linux containerd apt Amministrazione server