Installare Docker e Docker Compose su Ubuntu 26.04 e Debian 13 dal repository ufficiale

Docker è il modo più rapido per far girare servizi da self-hosting come Nextcloud, Pi-hole o un reverse proxy senza sporcare il sistema. Su Ubuntu 26.04 e Debian 13 conviene installarlo dal repository apt di Docker, che oggi distribuisce Docker Engine 29.9.3 insieme al plugin Compose v2: questa guida spiega come, come usarlo senza sudo e come evitare la trappola più seria, cioè le porte dei container che ignorano il firewall UFW.

In breve

  • Tempo: 15–20 minuti.
  • Difficoltà: facile.
  • Cosa serve: un server o PC a 64 bit con Ubuntu 26.04 LTS (o 24.04, 22.04, 26.10) oppure Debian 13 o 12, un utente con sudo, connessione a Internet.

Versioni di riferimento: Docker Engine 29.9.3 (3 novembre 2026), plugin Compose v2 dal pacchetto docker-compose-plugin, Ubuntu 26.04 LTS “Resolute Raccoon”, Debian 13 “trixie”. Procedura verificata sulla documentazione ufficiale al 4 novembre 2026.

Perché il repository di Docker e non snap o docker.io

Ubuntu offre tre strade: il pacchetto docker.io dei repository della distribuzione, lo snap e il repository di Docker. La documentazione di Docker considera docker.io, docker-compose, docker-compose-v2 e podman-docker pacchetti “non ufficiali” che vanno rimossi prima di installare quelli ufficiali, perché vanno in conflitto. Il repository di Docker ha due vantaggi pratici: versioni allineate alle note di rilascio ufficiali e il plugin Compose v2 che si aggiorna insieme al motore. Lo script di installazione rapida (get.docker.com) esiste, ma Docker stessa lo raccomanda solo per ambienti di test.

Le derivate di Ubuntu come Linux Mint non sono supportate ufficialmente, anche se spesso la procedura funziona.

Prerequisiti e backup

Attenzione: se sulla macchina avete già usato Docker installato in altro modo, immagini, container e volumi in /var/lib/docker/ non vengono cancellati rimuovendo i vecchi pacchetti, ma conviene comunque copiarli altrove prima di cambiare installazione. Sui server con dati in volumi Docker, fate prima un backup o uno snapshot.

Se il server è nuovo, completate prima il setup di base con utente non root e firewall, descritto nella guida https://hardwarfare.it/ai-software/linux/primo-setup-vps-ubuntu-ssh-firewall/.

Passo 1: rimuovere i pacchetti in conflitto

Su Ubuntu il comando ufficiale è:

sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc | cut -f1)

Su Debian l’elenco è lo stesso senza docker-compose-v2:

sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-doc docker-buildx podman-docker containerd runc | cut -f1)

È normale che apt risponda che nessuno di questi pacchetti è installato.

Passo 2: aggiungere il repository apt di Docker

La chiave GPG va in /etc/apt/keyrings e la sorgente in formato deb822 in /etc/apt/sources.list.d/docker.sources. Su Ubuntu:

sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update

Su Debian cambiano solo tre righe: l’URL della chiave (https://download.docker.com/linux/debian/gpg), la riga URIs: https://download.docker.com/linux/debian e la riga Suites: $(. /etc/os-release && echo "$VERSION_CODENAME").

Verifica: l’output di sudo apt update deve contenere una riga che punta a download.docker.com, senza errori di firma. Con apt list --all-versions docker-ce vedete le versioni disponibili: in cima deve esserci la serie 29.9.

Passo 3: installare Docker Engine e Compose

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

Il servizio parte da solo. Se avete bisogno di bloccare una versione precisa (per esempio per allinearvi a un altro server), la documentazione mostra come passare la stringa di versione a docker-ce e docker-ce-cli, prendendola dall’elenco del passo precedente.

Verifica: sudo systemctl status docker deve mostrare active (running); se non è così, sudo systemctl start docker. Poi il test ufficiale: sudo docker run hello-world scarica un’immagine di prova, la esegue e stampa un messaggio di conferma. Infine docker compose version deve rispondere con la versione del plugin.

Passo 4: usare Docker senza sudo (sapendo cosa comporta)

L’installazione crea il gruppo docker, ma vuoto. Per aggiungere il vostro utente:

sudo usermod -aG docker $USER
newgrp docker
docker run hello-world

L’appartenenza al gruppo diventa definitiva al prossimo login; newgrp la attiva subito nella shell corrente.

Attenzione: la documentazione di Docker è esplicita: il gruppo docker concede privilegi equivalenti a root. Chi controlla quell’utente può montare l’intero filesystem in un container. Su un server condiviso aggiungete al gruppo solo account amministrativi. In alternativa esiste la modalità rootless, che fa girare il demone come utente normale: richiede il pacchetto uidmap e si configura con dockerd-rootless-setuptool.sh install (incluso in docker-ce-rootless-extras), con alcune limitazioni sulle reti e sulle porte basse.

Docker e containerd sono già abilitati all’avvio su Ubuntu e Debian. Se un giorno doveste riattivarli: sudo systemctl enable docker.service e sudo systemctl enable containerd.service.

Passo 5: limitare la crescita dei log

Il driver di log predefinito, json-file, di base non ruota i file: un container molto loquace può riempire il disco di un VPS piccolo in poche settimane. Si risolve creando /etc/docker/daemon.json:

sudo nano /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}
sudo systemctl restart docker

I valori numerici in log-opts vanno tra virgolette, come nell’esempio. La nuova impostazione vale solo per i container creati dopo il riavvio: quelli esistenti vanno ricreati (con Compose basta docker compose up -d --force-recreate).

Passo 6: il primo compose.yaml, commentato

Creiamo un servizio minimo, un Nginx che serve una pagina statica, pensato già per non esporsi a Internet:

mkdir -p ~/stack/prova/html
cd ~/stack/prova
echo 'Ciao da Docker' > html/index.html
nano compose.yaml
services:
  web:
    # immagine ufficiale di Nginx, variante leggera
    image: nginx:alpine
    # 127.0.0.1 davanti alla porta: raggiungibile solo dal server stesso
    ports:
      - "127.0.0.1:8080:80"
    # la cartella html locale diventa la radice del sito, in sola lettura
    volumes:
      - ./html:/usr/share/nginx/html:ro
    # riparte da solo dopo un riavvio, salvo stop manuale
    restart: unless-stopped
docker compose up -d
docker compose ps

Verifica: curl http://127.0.0.1:8080 sul server deve rispondere Ciao da Docker. Dall’esterno, invece, http://IP_SERVER:8080 non deve rispondere: è il comportamento voluto.

Il problema serio: Docker scavalca UFW

Questo è il punto che le guide italiane saltano più spesso. La documentazione di Docker lo scrive chiaramente: se usate UFW o firewalld, le porte che pubblicate con Docker ignorano le regole del firewall. Docker instrada il traffico dei container nella tabella nat, prima che i pacchetti arrivino alle catene INPUT e OUTPUT usate da UFW. Risultato: con ports: - "8080:80" il servizio è aperto a tutta Internet anche se ufw status dice il contrario.

Le soluzioni, in ordine di semplicità:

  1. Pubblicare solo su localhost, come nell’esempio: "127.0.0.1:8080:80". Il servizio è raggiungibile solo dal server, e davanti mettete un reverse proxy (Nginx, Caddy o Nginx Proxy Manager) che espone 80 e 443, le uniche porte da aprire in UFW. Docker avverte che nelle versioni precedenti alla 28.0.0 anche gli host sulla stessa rete locale potevano raggiungere le porte pubblicate su localhost: con la 29.x il problema non c’è.
  2. Cambiare l’indirizzo predefinito con "ip": "127.0.0.1" in daemon.json. Attenzione al limite: vale solo per la rete bridge predefinita, mentre Compose crea reti proprie per ogni progetto. Per questo la regola esplicita nel compose.yaml resta la scelta più affidabile.
  3. Regole nella catena DOCKER-USER, per chi deve esporre una porta solo a certi indirizzi. Docker supporta solo iptables-nft e iptables-legacy: le regole scritte direttamente con nft non sono supportate su un sistema con Docker.

Sconsigliamo invece di disattivare la gestione del firewall di Docker ("iptables": false): la documentazione stessa avverte che di solito rompe la rete dei container.

Log e aggiornamento dei container

docker compose logs -f --tail 50
docker compose pull
docker compose up -d
docker image prune

Il primo comando segue in diretta le ultime 50 righe di log. I tre successivi sono il ciclo di aggiornamento: scaricare le immagini nuove, ricreare solo i container cambiati, eliminare le vecchie immagini rimaste senza tag (per default docker image prune tocca solo quelle). Docker Engine invece si aggiorna con apt, ripetendo il comando del passo 3 dopo sudo apt update.

Se qualcosa va storto

  • Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?: il servizio è fermo o il vostro utente non ha ancora i permessi. Controllate con sudo systemctl status docker; se gira, avete aggiunto l’utente al gruppo ma non avete rifatto il login.
  • WARNING: Error loading config file: /home/user/.docker/config.json - stat /home/user/.docker/config.json: permission denied: capita se avete usato Docker con sudo prima di aggiungervi al gruppo. La correzione ufficiale è sudo chown "$USER":"$USER" /home/"$USER"/.docker -R seguito da sudo chmod g+rwx "$HOME/.docker" -R.
  • Errore di firma su apt update: la chiave in /etc/apt/keyrings/docker.asc manca o non è leggibile. Ripetete i comandi curl e chmod a+r del passo 2.
  • Un servizio è raggiungibile da fuori anche se UFW lo blocca: è il comportamento descritto sopra, non un guasto di UFW. Controllate le righe ports e aggiungete 127.0.0.1:.
  • Disco pieno: docker system df mostra quanto occupano immagini, container, volumi e cache di build.

Come tornare indietro

Per disinstallare completamente:

sudo apt purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
sudo rm /etc/apt/sources.list.d/docker.sources
sudo rm /etc/apt/keyrings/docker.asc

Attenzione: immagini, container e volumi restano in /var/lib/docker e /var/lib/containerd. La documentazione indica di cancellarli con sudo rm -rf /var/lib/docker e sudo rm -rf /var/lib/containerd, ma è un’operazione definitiva: i dati dei volumi vanno persi. Fatelo solo dopo aver salvato ciò che serve. Anche /etc/docker/daemon.json va rimosso a mano.

La nostra lettura

Installare Docker è la parte facile; il vero errore da evitare è pubblicare porte pensando che UFW le protegga. Prendete l’abitudine di scrivere sempre 127.0.0.1: davanti alle porte e di esporre solo un reverse proxy: è la base su cui costruire i servizi delle prossime guide, da https://hardwarfare.it/ai-software/linux/reverse-proxy-nginx-proxy-manager-caddy/ a https://hardwarfare.it/ai-software/linux/installare-nextcloud-docker-aio-https/ e https://hardwarfare.it/ai-software/linux/pi-hole-adguard-home-docker-rete/.

Articolo redatto con il supporto di strumenti di intelligenza artificiale e verificato dalla redazione. Come usiamo l'AI · Come lavoriamo

Redazione HardWarfare
Redazione HardWarfarehttps://hardwarfare.it/chi-siamo/
La redazione di HardWarfare segue hardware PC, gaming, smartphone, sicurezza informatica e mercato tech. Ogni articolo parte da fonti primarie linkate in calce, viene verificato da un revisore diverso da chi lo scrive e si aggiorna quando i fatti cambiano. Scriveteci da Contatti per segnalazioni e correzioni.

Da non perdere

Articoli correlati

LASCIA UN COMMENTO

Scrivi il tuo commento!
Inserisci il tuo nome