Installare Docker su Rocky Linux e AlmaLinux
Installa Docker Engine con dnf su Rocky Linux o AlmaLinux e risolvi due problemi tipici: Podman che occupa il comando docker e SELinux sui bind mount.
Installare Docker su Rocky Linux e AlmaLinux
Per installare Docker su Rocky Linux o AlmaLinux, aggiungi il repository dnf ufficiale di Docker, installa il motore con il plugin Compose, quindi abilita il servizio. Sono quattro comandi e la procedura è identica su entrambe le distribuzioni, perché entrambe sono ricostruzioni di Red Hat Enterprise Linux (RHEL) e condividono la relativa struttura dei pacchetti. CentOS Stream funziona nello stesso modo. Tutto ciò che segue si applica a entrambe; se devi ancora scegliere tra le due, i fattori decisivi sono l'impegno di compatibilità dichiarato da ciascun progetto e il supporto ancora disponibile per il tuo processore meno recente.
L'installazione è breve, quindi la maggior parte di questa guida descrive le differenze di Enterprise Linux (EL) rispetto a Ubuntu. Nell'immagine, Podman potrebbe già gestire il comando docker. SELinux blocca i file montati con bind mount finché non ricevono l'etichetta corretta. Firewalld non filtra le porte pubblicate da Docker, quindi una porta del container può essere esposta a Internet mentre firewall-cmd segnala che non c'è nulla in ascolto.
Non usare lo script di installazione semplificata di Docker disponibile all'indirizzo get.docker.com. La documentazione ufficiale di Docker indica che non è consigliato per la produzione. Modifica la configurazione dei repository senza chiedere conferma e non può essere eseguito nuovamente in sicurezza per gli aggiornamenti. Aggiungere manualmente il repository fa sì che dnf upgrade gestisca Docker come qualsiasi altro pacchetto del sistema. In questo modo il motore rientra anche nell'ambito di dnf-automatic, se lo usi per applicare gli aggiornamenti di sicurezza secondo una pianificazione, quindi decidi subito se vuoi aggiornare Docker senza intervento o mantenerlo fermo fino a una finestra di manutenzione. In entrambi i casi, un aggiornamento sostituisce il binario del pacchetto mentre il vecchio dockerd continua a essere in esecuzione, e needs-restarting è il comando che indica quali servizi stanno ancora eseguendo il codice appena sostituito.
Podman risponde già al comando docker?
Rocky Linux e AlmaLinux includono podman nei repository predefiniti e molte immagini VPS lo installano automaticamente. Alcune immagini fanno un passo ulteriore e installano podman-docker, che colloca uno script shell in /usr/bin/docker e lo usa per chiamare podman. Ogni comando docker che si digita esegue quindi podman, invece di Docker. Di conseguenza, una guida scritta per Docker può produrre un output inatteso.
Il primo indizio è un banner. Lo script /usr/bin/docker verifica l'esistenza del file /etc/containers/nodocker. Se il file non esiste, visualizza una riga prima di eseguire qualsiasi operazione:
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.Qualcuno potrebbe avere creato quel file per nascondere il banner, quindi non basarsi soltanto su questo controllo. Chiedere al database dei pacchetti quale pacchetto possiede il binario:
command -v docker
rpm -qf "$(command -v docker)"Una risposta che inizia con podman-docker indica che a rispondere è podman. Una risposta che inizia con docker-ce-cli indica il Docker reale. Se rpm -qf segnala che nessun pacchetto possiede il file, qualcuno lo ha installato manualmente. In questo caso, leggere lo script prima di considerarlo attendibile.
Podman esegue le stesse immagini OCI ed è una scelta valida. Se si desidera usare podman, ci si può fermare qui. Entrambi sono motori per container Linux. Se la scelta della piattaforma è ancora aperta, è utile sapere che i jail di FreeBSD isolano un userland completo invece di eseguire immagini stratificate scaricate da un registry. Se si desidera Docker Engine, rimuovere prima i pacchetti in conflitto. Questo è l'elenco documentato da Docker per RHEL:
sudo dnf remove docker docker-client docker-client-latest docker-common \
docker-latest docker-latest-logrotate docker-logrotate docker-engine podman runcLeggere cosa prevede di rimuovere dnf prima di confermare. In una nuova immagine VPS, l'elenco è breve. Su un server già utilizzato, la rimozione di podman può rimuovere anche cockpit-podman o un altro strumento che dipende da esso.
In linea di principio è possibile mantenere podman insieme a Docker: rimuovere soltanto podman-docker, in modo da liberare il nome docker, e runc, che viene sostituito dal pacchetto containerd.io. La documentazione di Docker considera podman un pacchetto in conflitto, quindi Docker non supporta questa configurazione. Se l'installazione continua a segnalare un conflitto, usare l'elenco completo di rimozione riportato sopra.
Aggiungere il repository di Docker con dnf config-manager
Docker pubblica pacchetti RPM per Enterprise Linux all'indirizzo download.docker.com. Il file del repository punta all'albero CentOS, che è quello utilizzato da Rocky Linux e AlmaLinux. Indicare un repository CentOS su un sistema Rocky sembra un errore, finché non si conosce l'origine comune delle due distribuzioni nella linea CentOS, dopo che Red Hat ha trasformato CentOS in Stream nel 2020. Verificato ad agosto 2026, Docker documenta questo repository per CentOS Stream 9 e CentOS Stream 10.
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repoLa versione 5 di dnf ha rimosso l'argomento --add-repo, quindi il secondo comando non funziona nelle versioni più recenti. Controllare quale versione è installata e usare la sintassi corrispondente:
dnf --versionSe viene visualizzata una versione 5.x, usare invece la forma con il sottocomando:
sudo dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/centos/docker-ce.repoEntrambe le forme scrivono lo stesso file in /etc/yum.repos.d/docker-ce.repo. La forma errata restituisce un errore relativo a un argomento sconosciuto, invece di eseguire un'operazione errata senza segnalarlo. L'errore sarà quindi evidente.
Il file del repository imposta baseurl su un percorso che contiene $releasever, e dnf espande questa variabile usando il pacchetto che identifica la release. Rocky Linux e AlmaLinux la impostano sul numero della versione principale: 9 su EL 9 e 10 su EL 10. Per questo motivo un repository CentOS viene risolto correttamente su un sistema Rocky. Verificare l'espansione prima dell'installazione:
sudo dnf repoinfo docker-ce-stableLeggere la riga Repo-baseurl. Dovrebbe terminare con /9/x86_64/stable o /10/x86_64/stable. Se la release imposta $releasever su una versione puntuale come 9.6, dnf segnala Status code: 404 per quell'URL quando scarica i metadati. Correggere il problema modificando /etc/yum.repos.d/docker-ce.repo e sostituendo $releasever con il solo numero della versione principale.
Installare il motore e il plugin Compose
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginCinque pacchetti, ciascuno con una funzione specifica. docker-ce è il demone, dockerd. docker-ce-cli è il comando docker che si usa nel terminale. containerd.io è il runtime dei container gestito dal demone. docker-buildx-plugin crea le immagini. docker-compose-plugin fornisce docker compose come sottocomando.
Questi pacchetti non installano un binario docker-compose con il trattino. Quello apparteneva a Compose v1, che ha raggiunto il fine vita a luglio 2023. Qualsiasi elemento che richiami docker-compose con un trattino deve essere aggiornato a docker compose con uno spazio.
La prima installazione si interrompe per importare la chiave di firma di Docker e ne mostra l'impronta digitale. La chiave proviene da gpgkey=https://download.docker.com/linux/centos/gpg nel file del repository appena aggiunto. Prima di accettarla, confronta l'impronta visualizzata da dnf con quella indicata da quell'URL.
Un errore si verifica abbastanza spesso da meritare una verifica specifica. Se dnf segnala che containerd.io richiede container-selinux e che nessun pacchetto lo fornisce, il repository AppStream è disabilitato. Esegui dnf repolist e verifica che appstream sia presente nell'elenco, perché è il repository da cui container-selinux viene distribuito su EL 9 e EL 10.
Avviare Docker e verificare che sia in esecuzione
sudo systemctl enable --now docker
sudo systemctl status docker --no-pager
sudo docker run --rm hello-worldI pacchetti RPM di Docker lasciano il daemon arrestato e disabilitato dopo l'installazione. Per questo questo passaggio compare nella pagina Docker per CentOS, ma non in quella per Ubuntu, dove il pacchetto deb avvia automaticamente il servizio. Se si salta enable, Docker resta in esecuzione fino al riavvio successivo; poi rimane arrestato e arresta anche tutti i container.
systemctl status dovrebbe mostrare Active: active (running). Il container hello-world dovrebbe stampare This message shows that your installation appears to be working correctly. e terminare. Se invece stampa un errore di autorizzazione su /var/run/docker.sock, significa che manca sudo. La sezione seguente relativa al gruppo docker risolve il problema.
Verificare separatamente il plugin Compose, perché è un pacchetto distinto e può mancare anche quando il motore funziona:
docker compose versionUn risultato corretto è simile a Docker Compose version v2.x.x. Il ripristino dei servizi dopo un riavvio è un problema distinto dall'abilitazione del daemon: le restart policy determinano se i servizi Compose vengono riavviati all'avvio.
Perché un bind mount restituisce «permission denied»?
Rocky Linux e AlmaLinux eseguono SELinux (Security-Enhanced Linux) in modalità enforcing per impostazione predefinita. Verificatelo con getenforce, che stampa Enforcing.
I container Docker vengono eseguiti con il tipo SELinux container_t e questo tipo può leggere e scrivere soltanto file etichettati con container_file_t. Una directory creata sull'host eredita l'etichetta assegnata dal percorso padre, che non è container_file_t. Il container viene quindi bloccato anche se proprietario, gruppo e permessi sembrano corretti dal lato dell'host. Riproducete il problema con tre comandi:
sudo mkdir -p /srv/site
echo hello | sudo tee /srv/site/index.html
sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro nginx:alpine cat /usr/share/nginx/html/index.htmlIl container stampa:
cat: can't open '/usr/share/nginx/html/index.html': Permission deniedDue comandi mostrano la causa. ls -ldZ /srv/site stampa l'etichetta, che per un percorso sotto /srv è system_u:object_r:var_t:s0, non container_file_t. Poi sudo ausearch -m avc -ts recent stampa il record di audit del kernel, che contiene avc: denied { read }, un campo scontext= che indica container_t e un campo tcontext= che indica l'etichetta appena visualizzata sulla directory. La mancata corrispondenza tra questi due campi spiega interamente il problema.
La correzione consiste nell'aggiungere un suffisso all'argomento del volume. Docker rietichetta il percorso automaticamente:
sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro,z nginx:alpine cat /usr/share/nginx/html/index.html:z in lettere minuscole rietichetta il contenuto come condiviso, quindi più container possono usare la stessa directory. :Z in lettere maiuscole lo rietichetta come privato e non condiviso, associandolo a un solo container; un secondo container che legge lo stesso percorso viene quindi bloccato. Usate :z per tutto ciò che viene utilizzato anche da un sidecar o da un container di backup. Usate :Z per una directory di database gestita da un solo container.
La documentazione di Docker include un'avvertenza importante, perché la rietichettatura è ricorsiva. Eseguire il bind mount di una directory di sistema come /home o /usr con :Z «rende inutilizzabile la macchina host e potrebbe essere necessario rietichettare manualmente i file della macchina host». Applicate questi suffissi alle directory create per il container, mai a un percorso di sistema.
In Compose il suffisso va aggiunto alla stessa stringa:
services:
web:
image: nginx:alpine
volumes:
- /srv/site:/usr/share/nginx/html:ro,zÈ facile incorrere in due limitazioni. Il flag --mount non può impostare un'etichetta SELinux; usate quindi -v quando ne serve una. I volumi denominati non richiedono alcun suffisso, perché Docker assegna autonomamente le etichette alle directory che crea sotto /var/lib/docker/volumes.
Non disattivate SELinux. Usate sudo setenforce 0 soltanto come test di un minuto: se il container funziona, il problema riguarda un'etichetta e :z è la soluzione. Riattivatelo subito con sudo setenforce 1. Su Enterprise Linux, permission denied in un bind mount può avere due cause distinte che dall'interno del container risultano identiche. Una è l'etichetta SELinux. L'altra è la normale proprietà numerica di utente e gruppo, che è ciò che le variabili PUID e PGID servono a risolvere. ls -lnZ mostra in una sola riga i permessi, il proprietario numerico e l'etichetta, così potete capire quale dei due problemi state affrontando.
Perché una porta pubblicata è raggiungibile quando firewalld sembra chiuso?
Firewalld è il firewall predefinito su Rocky Linux e AlmaLinux. Verifica che sia in esecuzione con sudo systemctl is-active firewalld. Se non lo hai ancora configurato su questo server, devi prima aprire SSH e una porta web con firewalld, perché il problema descritto di seguito è comprensibile solo dopo aver configurato un ruleset di zona funzionante con cui fare il confronto. Ora pubblica una porta e verifica quali porte risultano aperte secondo firewalld:
sudo docker run -d --name web -p 8080:80 nginx:alpine
sudo firewall-cmd --list-portsfirewall-cmd stampa una riga vuota. Da un altro computer, curl -I http://YOUR_SERVER_IP:8080/ restituisce HTTP/1.1 200 OK. La porta è aperta a Internet, ma il firewall non segnala nulla.
La causa è il percorso seguito dal pacchetto. Le regole di zona di firewalld filtrano il traffico indirizzato direttamente all'host. Una porta pubblicata non è indirizzata all'host: Docker installa una regola di destination NAT (network address translation) che riscrive la destinazione con l'indirizzo del container prima che il pacchetto raggiunga il percorso di input dell'host. Il kernel inoltra quindi il pacchetto invece di consegnarlo localmente. Docker inserisce inoltre le interfacce bridge in una zona di firewalld chiamata docker, il cui target è ACCEPT, e aggiunge una policy di forwarding chiamata docker-forwarding che consente l'inoltro da qualsiasi zona verso la zona docker. Le regole della tua zona non vedono mai il pacchetto.
La soluzione più pulita non richiede alcuna regola del firewall. Associa il lato host della pubblicazione all'interfaccia loopback e metti un reverse proxy davanti al servizio:
sudo docker rm -f web
sudo docker run -d --name web -p 127.0.0.1:8080:80 nginx:alpine
curl -I http://127.0.0.1:8080/Il comando locale curl restituisce HTTP/1.1 200 OK e la stessa richiesta eseguita da un altro computer non riesce più a connettersi. Se nell'argomento -p non specifichi alcun indirizzo host, la porta viene pubblicata su tutte le interfacce. Considera quindi un valore -p 8080:80 senza indirizzo come una scelta di esposizione pubblica del servizio.
Quando hai bisogno che un servizio sia raggiungibile da alcuni indirizzi ma non da altri, Docker mette a disposizione una chain riservata. DOCKER-USER viene elaborata prima delle regole di accept di Docker. Una regola inserita in questa chain rimane quindi valida anche quando Docker riavvia e riscrive le proprie chain:
sudo iptables -I DOCKER-USER -i enp1s0 ! -s 203.0.113.10 -j DROP
sudo iptables -S DOCKER-USERRicava il nome dell'interfaccia da ip route show default invece di presumere eth0, perché le immagini EL attuali usano nomi come enp1s0 o ens3. Su Rocky e AlmaLinux, il comando iptables è un livello di compatibilità sopra nftables e rende visibili le chain di Docker. Le regole aggiunte in questo modo vengono eliminate dopo un riavvio se non le salvi. Quando hai verificato che funzionano, inseriscile quindi in un'unità systemd.
Docker Engine 28.0, rilasciato nel 2025, ha risolto un problema correlato: l'accesso diretto tramite routing alle porte dei container mai pubblicate è ora bloccato nella chain DOCKER. La modifica non riguarda le porte pubblicate, quindi tutto quanto descritto sopra resta valido nelle versioni attuali. È utile adottare una procedura operativa: dopo ogni sudo firewall-cmd --reload, verifica nuovamente una porta pubblicata. Se non risponde più, sudo systemctl restart docker reinstalla le regole di Docker.
Gli amministratori Ubuntu incontrano lo stesso problema tramite uno strumento diverso, come spiegato in perché le porte Docker pubblicate ignorano le regole di ufw. In entrambi i casi, la causa è il percorso NAT. Cambia soltanto il firewall che si trova davanti a tale percorso.
Aggiungere un utente non root al gruppo docker
Digitare sudo prima di ogni comando docker diventa presto scomodo. Il gruppo docker elimina questa necessità:
sudo usermod -aG docker $USER
newgrp docker
docker run --rm hello-worldusermod -aG modifica /etc/group, ma la shell corrente contiene già il proprio elenco di gruppi. La modifica diventa effettiva solo quando si avvia una nuova shell. newgrp docker avvia una shell con il gruppo associato, così è possibile eseguire subito un test. Le nuove sessioni SSH lo rilevano automaticamente.
È importante comprendere quali autorizzazioni concede questo gruppo. L'appartenenza al gruppo fornisce accesso in scrittura a /var/run/docker.sock. Qualsiasi processo che può comunicare con questo socket può chiedere al daemon di avviare un container con il filesystem dell'host montato al suo interno. Un comando mostra le conseguenze:
docker run --rm -v /:/host alpine wc -l /host/etc/shadowIl comando legge un file accessibile soltanto a root, usando un account senza autorizzazioni sudo. La documentazione post-installazione di Docker afferma lo stesso: il gruppo docker concede privilegi equivalenti a quelli di root. Aggiungere un account a questo gruppo solo se si concederebbe allo stesso account anche sudo. Se si stanno configurando account su un nuovo server, questa decisione va presa insieme al resto della configurazione di un utente con privilegi minimi su un VPS, non in un secondo momento.
Docker offre anche una modalità rootless, che esegue il daemon come utente senza privilegi. È un percorso di installazione separato e modifica il comportamento dei driver di storage e delle porte inferiori a 1024. Va quindi pianificata come un progetto autonomo, non come un flag da aggiungere in seguito.
Dove andare ora
Ora hai il motore, il plugin Compose, un servizio che resta attivo dopo il riavvio e i tre comportamenti specifici di EL documentati sopra. Il passaggio successivo consiste nell'avere un compose.yaml per servizio. La struttura di un file Compose illustra il formato del file e i comandi che lo gestiscono. Se questo è il tuo primo host per container, Eseguire Docker su un VPS tratta gli aspetti relativi a dimensionamento, archiviazione e gestione delle immagini che questa guida non approfondisce.
FAQ
Il repository Docker per CentOS funziona su Rocky Linux e AlmaLinux?
Sì. Aggiungi https://download.docker.com/linux/centos/docker-ce.repo con dnf config-manager. Il baseurl in quel file contiene $releasever, che Rocky Linux e AlmaLinux espandono nel numero della versione principale. Pertanto, un sistema EL 9 usa l'albero CentOS 9 e un sistema EL 10 usa l'albero CentOS 10. Verifica l'espansione con sudo dnf repoinfo docker-ce-stable e leggi la riga Repo-baseurl. Un Status code: 404 durante il recupero dei metadati da parte di dnf indica che la variabile è stata espansa in una versione correttiva. Modifica /etc/yum.repos.d/docker-ce.repo usando il solo numero della versione principale per risolvere il problema.
È possibile installare Docker e podman sullo stesso server?
La documentazione di Docker indica podman e runc come pacchetti in conflitto e richiede di rimuoverli entrambi prima di installare Docker Engine. Il conflitto concreto riguarda il pacchetto podman-docker, che gestisce /usr/bin/docker e trasforma ogni comando docker in un comando podman. Esegui rpm -qf "$(command -v docker)" per verificare quale pacchetto gestisce quel percorso. Se l'output inizia con podman-docker, sta rispondendo podman. Docker non supporta l'uso combinato dei due engine. Su un server che svolge un ruolo importante, scegline uno.
Perché il container restituisce un errore di autorizzazione su un bind mount?
SELinux è in modalità enforcing per impostazione predefinita su Rocky Linux e AlmaLinux. I container vengono eseguiti con il tipo container_t e possono accedere solo ai file con l'etichetta container_file_t. Di conseguenza, una directory appena creata può avere l'etichetta errata e l'accesso viene negato indipendentemente dal proprietario e dai permessi. Verifica il problema con ls -ldZ sul percorso dell'host e con sudo ausearch -m avc -ts recent, che stampa avc: denied mostrando i due contesti non corrispondenti. Aggiungi :z all'argomento del volume per i contenuti condivisi tra container, oppure :Z per i contenuti privati di un singolo container. Non puntare mai :Z a /home o /usr, perché la rietichettatura è ricorsiva e danneggerebbe l'host.
Devo aprire una porta in firewalld per pubblicare la porta di un container?
No, ed è proprio questo il problema. La regola NAT di Docker riscrive l'indirizzo di destinazione prima che il pacchetto raggiunga il percorso di input dell'host. Di conseguenza, le regole delle zone di firewalld non lo analizzano. Docker inserisce inoltre i propri bridge in una zona firewalld chiamata docker con destinazione ACCEPT. Un container avviato con -p 8080:80 è raggiungibile da Internet, mentre sudo firewall-cmd --list-ports non stampa nulla. Usa -p 127.0.0.1:8080:80 per pubblicare il servizio su un indirizzo specifico quando deve essere raggiungibile soltanto dall'host, oppure inserisci le regole di filtraggio nella catena DOCKER-USER, che Docker elabora prima delle proprie regole di accettazione.
Aggiungere il mio utente al gruppo docker è sicuro?
Concede privilegi root. Un membro del gruppo docker può scrivere in /var/run/docker.sock e docker run --rm -v /:/host alpine wc -l /host/etc/shadow può quindi leggere un file accessibile solo a root da un account privo di autorizzazioni sudo. La documentazione post-installazione di Docker dichiara la stessa equivalenza. Aggiungi solo account a cui concederesti già sudo e continua a usare sudo docker per gli account condivisi o di servizio. La modalità rootless è l'alternativa quando servono container eseguiti da un utente senza privilegi. Richiede un percorso di installazione separato, non una semplice impostazione.