Installer Docker sur Rocky Linux ou AlmaLinux
Installez Docker Engine avec dnf et Compose sur Rocky Linux ou AlmaLinux. Évitez les pièges de Podman, de SELinux sur les bind mounts et de firewalld.
Installer Docker sur Rocky Linux et AlmaLinux
Pour installer Docker sur Rocky Linux ou AlmaLinux, ajoutez le dépôt dnf de Docker, installez le moteur avec le plugin Compose, puis activez le service. Cette procédure tient en quatre commandes. Elle est identique sur les deux distributions, car elles sont toutes deux des reconstructions de Red Hat Enterprise Linux (RHEL) et utilisent la même organisation des paquets. CentOS Stream fonctionne de la même manière. Tout ce qui suit s’applique aux deux distributions. Si vous hésitez encore entre elles, les critères décisifs sont les engagements de compatibilité pris par chaque projet et la prise en charge de votre ancien processeur.
L’installation est courte. L’essentiel de ce guide porte donc sur les différences entre Enterprise Linux (EL) et Ubuntu. Podman peut déjà utiliser la commande docker sur votre image. SELinux bloque les fichiers montés avec bind tant qu’ils n’ont pas le bon label. Firewalld ne filtre pas les ports publiés par Docker. Un port de conteneur peut donc être ouvert sur Internet alors que firewall-cmd indique qu’aucun port n’est ouvert.
N’utilisez pas le script d’installation simplifiée de Docker depuis get.docker.com. La documentation de Docker indique qu’il n’est pas recommandé en production. Il réécrit la configuration de vos dépôts sans demander confirmation et ne peut pas être réexécuté sans risque pour effectuer une mise à niveau. Ajouter le dépôt manuellement permet à dnf upgrade de traiter Docker comme n’importe quel autre paquet du système. Le moteur entre également dans le périmètre de dnf-automatic, si vous l’utilisez pour appliquer automatiquement les mises à jour de sécurité, alors décidez rapidement si Docker doit être mis à jour sans intervention ou conservé jusqu’à une fenêtre de maintenance. Dans les deux cas, une mise à niveau remplace le binaire du paquet, tandis que l’ancien dockerd continue de fonctionner. needs-restarting est la commande qui indique quels services utilisent encore le code que vous venez de remplacer.
Podman répond-il déjà à la commande docker ?
Rocky Linux et AlmaLinux incluent podman dans leurs dépôts par défaut, et de nombreuses images VPS l’installent automatiquement. Certaines vont plus loin et installent podman-docker, qui place un script shell à /usr/bin/docker et appelle podman. Chaque commande docker que vous saisissez exécute alors podman à la place. Un guide écrit pour Docker produit donc une sortie inattendue.
Le premier indice est une bannière. Le script /usr/bin/docker vérifie la présence du fichier /etc/containers/nodocker. Si ce fichier est absent, il affiche une ligne avant d’exécuter quoi que ce soit :
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.Quelqu’un a peut-être créé ce fichier pour masquer la bannière. Ne vous y fiez donc pas seul. Demandez à la base de données des paquets quel paquet possède le binaire :
command -v docker
rpm -qf "$(command -v docker)"Une réponse qui commence par podman-docker signifie que podman répond. Une réponse qui commence par docker-ce-cli signifie qu’il s’agit du vrai Docker. Si rpm -qf indique qu’aucun paquet ne possède le fichier, quelqu’un l’a installé manuellement. Lisez alors le script avant de lui faire confiance.
Podman exécute les mêmes images OCI et constitue un choix raisonnable. Si vous souhaitez l’utiliser, vous pouvez vous arrêter ici. Tous deux sont des moteurs de conteneurs Linux. Si le choix de la plateforme n’est pas encore arrêté, sachez que les jails FreeBSD isolent un userland complet au lieu d’exécuter des images en couches téléchargées depuis un registre. Si vous voulez Docker Engine, supprimez d’abord les paquets en conflit. Voici la liste indiquée par la documentation Docker pour RHEL :
sudo dnf remove docker docker-client docker-client-latest docker-common \
docker-latest docker-latest-logrotate docker-logrotate docker-engine podman runcExaminez ce que dnf prévoit de supprimer avant de confirmer. Sur une image VPS neuve, la liste est courte. Sur un serveur déjà utilisé, la suppression de podman peut supprimer cockpit-podman ou un autre outil qui en dépend.
Conserver podman à côté de Docker est possible en théorie : supprimez uniquement podman-docker, afin de libérer le nom docker, ainsi que runc, que le paquet containerd.io remplace. La documentation Docker considère podman comme un paquet en conflit. Docker ne prend donc pas en charge cette configuration. Si l’installation signale toujours un conflit, utilisez la liste complète de suppression ci-dessus.
Ajouter le dépôt Docker avec dnf config-manager
Docker publie des RPM pour Enterprise Linux à l’adresse download.docker.com. Le fichier du dépôt pointe vers l’arborescence CentOS, celle que Rocky Linux et AlmaLinux utilisent pour résoudre les paquets. Pointer une machine Rocky vers un dépôt CentOS semble être une erreur, jusqu’à ce que l’on sache comment les deux distributions sont issues de la lignée CentOS après que Red Hat a transformé CentOS en Stream en 2020. Vérifié en août 2026, Docker documente ce dépôt pour CentOS Stream 9 et 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 version 5 de dnf a supprimé l’argument --add-repo. La deuxième commande échoue donc avec les versions récentes. Vérifiez la version installée, puis utilisez la forme correspondante :
dnf --versionSi la commande affiche une version 5.x, utilisez plutôt la forme avec sous-commande :
sudo dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/centos/docker-ce.repoLes deux formes écrivent le même fichier dans /etc/yum.repos.d/docker-ce.repo. La mauvaise forme échoue avec une erreur d’argument inconnu. Elle ne produit pas un résultat incorrect sans avertissement, vous ne risquez donc pas de passer à côté du problème.
Ce fichier de dépôt définit baseurl avec un chemin contenant $releasever. dnf développe cette variable à partir de votre paquet de version. Rocky Linux et AlmaLinux lui attribuent le numéro de version majeure. Vous obtenez donc 9 sur EL 9 et 10 sur EL 10. C’est la raison pour laquelle un dépôt CentOS est correctement résolu sur une machine Rocky. Vérifiez le développement de la variable avant l’installation :
sudo dnf repoinfo docker-ce-stableLisez la ligne Repo-baseurl. Elle doit se terminer par /9/x86_64/stable ou /10/x86_64/stable. Si votre version définit $releasever avec une version mineure telle que 9.6, dnf signale Status code: 404 pour cette URL lors de la récupération des métadonnées. Corrigez ce problème en modifiant /etc/yum.repos.d/docker-ce.repo et en remplaçant $releasever par le seul numéro de version majeure.
Installer le moteur et le plugin Compose
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginCinq paquets, chacun avec un rôle précis. docker-ce est le daemon, dockerd. docker-ce-cli correspond à la commande docker que vous saisissez. containerd.io est le runtime de conteneurs piloté par le daemon. docker-buildx-plugin construit les images. docker-compose-plugin fournit docker compose en tant que sous-commande.
Ces paquets n’installent pas de binaire docker-compose avec un trait d’union. Il s’agissait de Compose v1, arrivé en fin de vie en juillet 2023. Tout ce qui appelle docker-compose avec un trait d’union doit être mis à jour pour utiliser docker compose avec un espace.
La première installation s’interrompt pour importer la clé de signature Docker et en affiche l’empreinte. La clé provient de gpgkey=https://download.docker.com/linux/centos/gpg dans le fichier du dépôt que vous venez d’ajouter. Comparez donc l’empreinte affichée par dnf avec cette URL avant de l’accepter.
Un échec revient assez souvent pour être mentionné. Si dnf indique que containerd.io requiert container-selinux et qu’aucun paquet ne le fournit, votre dépôt AppStream est désactivé. Exécutez dnf repolist et vérifiez que appstream apparaît dans la liste, car c’est là que container-selinux est fourni sur EL 9 et EL 10.
Démarrer Docker et vérifier son fonctionnement
sudo systemctl enable --now docker
sudo systemctl status docker --no-pager
sudo docker run --rm hello-worldLes paquets RPM de Docker laissent le daemon arrêté et désactivé après l’installation. C’est pourquoi cette étape figure sur la page CentOS de Docker, mais pas sur sa page Ubuntu, où le paquet deb démarre le service automatiquement. Si vous ignorez enable, Docker fonctionne jusqu’au prochain redémarrage, puis reste arrêté et entraîne l’arrêt de tous les conteneurs.
systemctl status doit afficher Active: active (running). Le conteneur hello-world doit afficher This message shows that your installation appears to be working correctly., puis s’arrêter. S’il affiche plutôt une erreur de permission concernant /var/run/docker.sock, vous avez omis sudo. La section suivante consacrée au groupe docker corrige ce problème.
Vérifiez séparément le plugin Compose, car il s’agit d’un autre paquet. Il peut être absent alors que le moteur fonctionne correctement :
docker compose versionUne réponse correcte ressemble à Docker Compose version v2.x.x. Récupérer vos services après un redémarrage est différent d’activer le daemon, et les politiques de redémarrage déterminent si les services Compose reviennent au démarrage.
Pourquoi un bind mount renvoie-t-il « permission denied » ?
Rocky Linux et AlmaLinux exécutent SELinux (Security-Enhanced Linux) en mode enforcing par défaut. Vérifiez-le avec getenforce, qui affiche Enforcing.
Les conteneurs Docker s’exécutent sous le type SELinux container_t. Ce type peut uniquement lire et écrire les fichiers portant le label container_file_t. Un répertoire que vous créez sur l’hôte reçoit le label de son chemin parent, qui n’est pas container_file_t. Le conteneur reçoit donc un refus, même si le propriétaire, le groupe et les permissions semblent corrects depuis l’hôte. Reproduisez le problème avec trois commandes :
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.htmlLe conteneur affiche :
cat: can't open '/usr/share/nginx/html/index.html': Permission deniedDeux commandes indiquent la cause. ls -ldZ /srv/site affiche le label. Pour un chemin situé sous /srv, ce label est system_u:object_r:var_t:s0, et non container_file_t. Ensuite, sudo ausearch -m avc -ts recent affiche l’enregistrement d’audit du noyau. Celui-ci contient avc: denied { read }, un champ scontext= qui indique container_t, ainsi qu’un champ tcontext= qui indique le label que vous venez de voir sur le répertoire. Toute l’explication se trouve dans la différence entre ces deux champs.
La correction consiste à ajouter un suffixe à l’argument du volume. Docker modifie le label du chemin pour vous :
sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro,z nginx:alpine cat /usr/share/nginx/html/index.html:z en minuscules modifie le label du contenu en mode partagé. Plusieurs conteneurs peuvent ainsi utiliser le même répertoire. :Z en majuscules modifie le label en mode privé et non partagé. Le répertoire est alors associé à un seul conteneur, et un second conteneur qui lit le même chemin reçoit un refus. Utilisez :z pour tout répertoire également utilisé par un sidecar ou un conteneur de sauvegarde. Utilisez :Z pour un répertoire de base de données appartenant à un seul conteneur.
La documentation Docker contient un avertissement qu’il est important de rappeler, car la modification du label est récursive. Monter un répertoire système tel que /home ou /usr avec :Z « rend votre machine hôte inutilisable et vous devrez peut-être modifier manuellement le label des fichiers de la machine hôte ». Utilisez ces suffixes uniquement avec les répertoires que vous avez créés pour le conteneur, jamais avec un chemin système.
Dans Compose, le suffixe s’ajoute à la même chaîne :
services:
web:
image: nginx:alpine
volumes:
- /srv/site:/usr/share/nginx/html:ro,zDeux limites sont faciles à oublier. L’option --mount ne peut pas définir de label SELinux. Utilisez donc -v lorsque vous en avez besoin. Les volumes nommés n’ont pas besoin de suffixe, car Docker modifie lui-même le label des répertoires qu’il crée sous /var/lib/docker/volumes.
Ne désactivez pas SELinux. Utilisez sudo setenforce 0 uniquement comme test d’une minute. Si le conteneur fonctionne ensuite, le problème vient d’un label et :z est la solution. Rétablissez immédiatement le mode précédent avec sudo setenforce 1. Sur Enterprise Linux, permission denied sur un bind mount peut avoir deux causes distinctes qui semblent identiques depuis le conteneur. La première est le label SELinux. La seconde est la propriété numérique habituelle de l’utilisateur et du groupe. C’est ce que les variables PUID et PGID permettent de corriger. ls -lnZ affiche sur une seule ligne les permissions, le propriétaire numérique et le label. Vous pouvez ainsi déterminer lequel des deux problèmes vous devez corriger.
Pourquoi un port publié reste-t-il accessible alors que firewalld semble fermé ?
Firewalld est le pare-feu par défaut de Rocky Linux et AlmaLinux. Vérifiez qu’il fonctionne avec sudo systemctl is-active firewalld. Si vous ne l’avez pas encore configuré sur ce serveur, commencez par ouvrir SSH et un port web avec firewalld, car le problème ci-dessous n’a de sens que si vous disposez déjà d’un ruleset de zone fonctionnel auquel vous pouvez le comparer. Publiez maintenant un port et vérifiez ce que firewalld considère comme ouvert :
sudo docker run -d --name web -p 8080:80 nginx:alpine
sudo firewall-cmd --list-portsfirewall-cmd affiche une ligne vide. Depuis une autre machine, curl -I http://YOUR_SERVER_IP:8080/ renvoie HTTP/1.1 200 OK. Le port est ouvert sur Internet, alors que votre pare-feu ne signale rien.
La cause vient du chemin suivi par le paquet. Les règles de zone de firewalld filtrent le trafic destiné directement à l’hôte. Un port publié n’est pas destiné à l’hôte : Docker installe une règle de destination NAT (network address translation) qui réécrit la destination vers l’adresse du conteneur avant que le paquet n’atteigne le chemin d’entrée de l’hôte. Le kernel transmet donc le paquet au lieu de le remettre localement. Docker place ensuite ses interfaces bridge dans une zone firewalld appelée docker, dont la cible est ACCEPT, et ajoute une policy de forwarding appelée docker-forwarding, qui autorise le forwarding depuis n’importe quelle zone vers la zone docker. Vos règles de zone ne voient jamais passer le paquet.
La solution la plus propre ne nécessite aucune règle de pare-feu. Liez le côté hôte de la publication à loopback et placez un reverse proxy devant :
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/Le curl local renvoie HTTP/1.1 200 OK, et la même requête depuis une autre machine ne se connecte plus. Si aucune adresse hôte n’est indiquée dans l’argument -p, le port est publié sur toutes les interfaces. Considérez donc un -p 8080:80 seul comme une décision d’exposer publiquement ce service.
Si vous devez rendre un service accessible depuis certaines adresses et pas depuis d’autres, Docker vous réserve une chain. DOCKER-USER est traitée avant les règles accept de Docker. Une règle que vous y placez reste donc en place lorsque Docker redémarre et réécrit ses chains :
sudo iptables -I DOCKER-USER -i enp1s0 ! -s 203.0.113.10 -j DROP
sudo iptables -S DOCKER-USERRécupérez le nom de l’interface avec ip route show default au lieu de supposer eth0, car les images EL actuelles utilisent des noms comme enp1s0 ou ens3. Sur Rocky et AlmaLinux, la commande iptables fournit une couche de compatibilité au-dessus de nftables, et les chains de Docker y sont visibles. Les règles ajoutées de cette manière disparaissent après un redémarrage si vous ne les enregistrez pas. Écrivez-les donc dans une unité systemd lorsque leur comportement vous convient.
Docker Engine 28.0, publié en 2025, a corrigé un problème voisin : l’accès routé direct aux ports de conteneurs qui n’étaient jamais publiés est désormais bloqué dans la chain DOCKER. Cette modification ne concerne pas les ports publiés. Tout ce qui précède reste donc valable avec les versions actuelles. Adoptez une habitude opérationnelle : après chaque sudo firewall-cmd --reload, testez à nouveau un port publié. S’il ne répond plus, sudo systemctl restart docker réinstalle les règles de Docker.
Les administrateurs Ubuntu rencontrent le même problème avec un autre outil : pourquoi les ports Docker publiés ignorent les règles ufw. Dans les deux cas, la cause est le chemin NAT. Seul le pare-feu placé en amont change.
Ajouter un utilisateur non-root au groupe docker
Saisir sudo avant chaque commande docker devient vite fastidieux. Le groupe docker évite cette étape :
sudo usermod -aG docker $USER
newgrp docker
docker run --rm hello-worldusermod -aG modifie /etc/group, mais votre shell actuel possède déjà sa liste de groupes. La modification ne s’applique donc qu’à l’ouverture d’un nouveau shell. newgrp docker ouvre un shell avec le nouveau groupe, afin que vous puissiez tester immédiatement. Les nouvelles sessions SSH le prennent automatiquement en compte.
Soyez clair sur les droits accordés par ce groupe. Un membre obtient un accès en écriture à /var/run/docker.sock. Tout ce qui peut communiquer avec ce socket peut demander au daemon de démarrer un conteneur qui monte le système de fichiers de l’hôte. Une commande montre ce que cela implique :
docker run --rm -v /:/host alpine wc -l /host/etc/shadowCette commande lit un fichier accessible uniquement à root, depuis un compte qui ne possède aucun droit sudo. La documentation post-installation de Docker indique la même chose : le groupe docker accorde des privilèges équivalents à ceux de root. Ajoutez un compte à ce groupe uniquement si vous lui accorderiez également sudo. Si vous configurez des comptes sur un nouveau serveur, prenez cette décision en même temps que le reste de votre configuration d’un utilisateur avec le principe du moindre privilège sur un VPS, plutôt qu’après coup.
Docker propose également un mode rootless, qui exécute le daemon avec un utilisateur non privilégié. Il s’agit d’une procédure d’installation distincte. Elle modifie le comportement des pilotes de stockage et des ports inférieurs à 1024. Planifiez donc ce mode comme un projet à part entière, et non comme une option à ajouter ultérieurement.
Étapes suivantes
Vous disposez maintenant du moteur, du plugin Compose, d’un service qui redémarre automatiquement après un reboot et des trois comportements spécifiques à EL documentés ci-dessus. L’étape suivante consiste à créer une compose.yaml pour chaque service. L’anatomie d’un fichier Compose présente le format du fichier et les commandes qui le pilotent. S’il s’agit de votre premier hôte de conteneurs, exécuter Docker sur un VPS traite les questions de dimensionnement, de stockage et d’hygiène des images qui ne sont pas abordées dans ce guide.
FAQ
Le dépôt CentOS de Docker fonctionne-t-il sur Rocky Linux et AlmaLinux ?
Oui. Ajoutez https://download.docker.com/linux/centos/docker-ce.repo avec dnf config-manager. Le baseurl de ce fichier contient $releasever, et Rocky Linux et AlmaLinux le remplacent par le numéro de version majeure. Ainsi, un système EL 9 utilise l’arborescence CentOS 9 et un système EL 10 l’arborescence CentOS 10. Vérifiez le remplacement avec sudo dnf repoinfo docker-ce-stable et lisez la ligne Repo-baseurl. Un Status code: 404 lorsque dnf récupère les métadonnées signifie que la variable a été remplacée par une version mineure. Modifiez /etc/yum.repos.d/docker-ce.repo pour utiliser uniquement le numéro de version majeure.
Docker et podman peuvent-ils être installés sur le même serveur ?
La documentation de Docker indique que podman et runc sont des packages en conflit et demande de les supprimer avant d’installer Docker Engine. Le conflit concret concerne le package podman-docker, qui possède /usr/bin/docker et transforme chaque commande docker en commande podman. Exécutez rpm -qf "$(command -v docker)" pour voir quel package possède ce chemin. Si la sortie commence par podman-docker, c’est podman qui répond. Docker ne prend pas en charge l’utilisation simultanée des deux moteurs. Sur un serveur important, choisissez donc l’un des deux.
Pourquoi mon conteneur reçoit-il une erreur « permission denied » sur un bind mount ?
SELinux est appliqué par défaut sur Rocky Linux et AlmaLinux. Les conteneurs s’exécutent avec le type container_t et ne peuvent accéder qu’aux fichiers portant l’étiquette container_file_t. Un répertoire que vous avez créé porte donc la mauvaise étiquette, et l’accès est refusé quels que soient son propriétaire et ses permissions. Vérifiez-le avec ls -ldZ sur le chemin de l’hôte et sudo ausearch -m avc -ts recent, qui affiche avc: denied avec les deux contextes différents. Ajoutez :z à l’argument de volume pour le contenu partagé entre conteneurs, ou :Z pour le contenu privé à un seul conteneur. Ne pointez jamais :Z vers /home ou /usr, car le changement d’étiquette est récursif et endommagera l’hôte.
Dois-je ouvrir un port dans firewalld pour publier le port d’un conteneur ?
Non, et c’est justement le problème. La règle NAT de Docker réécrit l’adresse de destination avant que le paquet n’atteigne le chemin d’entrée de l’hôte. Les règles de zone de firewalld ne l’inspectent donc jamais. Docker place également ses bridges dans une zone firewalld appelée docker, avec la cible ACCEPT. Un conteneur démarré avec -p 8080:80 est accessible depuis Internet, tandis que sudo firewall-cmd --list-ports n’affiche rien. Publiez le service sur une adresse précise avec -p 127.0.0.1:8080:80 lorsque seul l’hôte doit y accéder, ou insérez les règles de filtrage dans la chaîne DOCKER-USER, que Docker traite avant ses propres règles d’acceptation.
Est-il sûr d’ajouter mon utilisateur au groupe docker ?
Cela accorde les privilèges root. Un membre du groupe docker peut écrire dans /var/run/docker.sock, puis docker run --rm -v /:/host alpine wc -l /host/etc/shadow peut lire un fichier réservé à root depuis un compte qui ne dispose d’aucun droit sudo. La documentation de Docker après installation indique la même équivalence. N’ajoutez que les comptes auxquels vous accorderiez déjà sudo, et continuez à utiliser sudo docker pour les comptes partagés ou de service. Le mode rootless est l’autre solution lorsque vous avez besoin d’exécuter des conteneurs avec un utilisateur non privilégié. Il s’agit d’un chemin d’installation distinct, et non d’un simple paramètre.