Installer Docker via cloud-init
Voici les différentes manières d’installer Docker via cloud-init. L’intérêt est de pouvoir utiliser l’image cloud officielle d’une distribution et de la rendre prête à l’emploi avec Docker dès le premier démarrage.
Cela fonctionne partout où l’on peut passer des user-data à cloud-init (clouds publics et privés, Proxmox, Incus, VirtualBox…) ; pour ma part j’ai testé ce qui suit sur IncusOS avec la variante cloud de ses modèles de conteneur.
Note
Cet article suppose des connaissances sur le fonctionnement de cloud-init ; si les termes user-data et cloud-config ne vous évoquent rien, lisez l’Introduction à cloud-init dans la documentation officielle.Méthode universelle via le script
La méthode la plus universelle pour installer Docker est d’utiliser le script de commodité.
Cette méthode fonctionne sur toutes les distributions officiellement supportées par Docker Inc. (ce qui à ce jour exclut Rocky Linux et Alma Linux), et vient avec la mise en garde suivante de la part de Docker :
Only recommended for testing and development environments.
Sans données cloud-config
Si l’on n’a pas besoin d’utiliser de directives spécifiques au format cloud-config, alors on peut se contenter de la ligne suivante comme user-data :
#include https://get.docker.com
Le format include dit à cloud-init de télécharger l’URL et de traiter son contenu comme s’il était passé directement en user-data.
https://get.docker.com retourne un script shell, cloud-init le détecte via la première ligne (#!/bin/sh) et l’exécute directement, exactement comme si l’on avait passé le script lui-même en user-data.
C’est le user-data le plus minimal possible qui a pour avantage de laisser cloud-init gérer le téléchargement du script (pas besoin de curl), mais comme inconvénient d’empêcher l’utilisation de cloud-config pour installer des packages, configurer des utilisateurs etc.
Il existe des solutions plus complexes pour combiner les formats include et cloud-config, mais pour ma part ça m’est plus pratique d’intégrer directement le script dans cloud-config.
À l’intérieur de cloud-config
Pour exécuter le script d’installation automatisé de Docker dans le format cloud-config, vous pouvez utiliser l’extrait suivant :
#cloud-config
packages:
- curl
runcmd:
# Télécharge et exécute le script d'installation officiel Docker
- curl -fsSL https://get.docker.com -o /tmp/get-docker.sh
- sh /tmp/get-docker.sh
packages s’assure que curl est installé (ce n’était pas le cas dans les images LXC de Debian/Ubuntu jusqu’à ce que j’en fasse la requête), puis runcmd le télécharge et l’exécute.
Pourquoi il est préférable d’éviter curl -fsSL https://get.docker.com | sh et de le faire en deux étapes :
- quand
curlest lent ou que la connexion est instable,shcommence à exécuter le script pendant son téléchargement : si le flux est corrompu ou tronqué, vous exécutez un script incomplet, ce qui peut laisser le système dans un état incohérent - de plus
runcmdne capture pas bien les erreurs en pipe : sicurléchoue (réseau pas encore prêt au démarrage), le code de sortie peut être masqué parshet cloud-init ne détectera pas l’échec.
Testé et approuvé sur Ubuntu 24.04 & 26.04, Debian 13 et CentOS/RHEL 9-10 (pour Rocky il faut attendre que Docker Inc. se décide à construire les paquets).
Méthode via le dépôt officiel
On laisse derrière nous la méthode universelle via le script pour passer à la méthode recommandée pour la production.
Elle requiert une configuration manuelle du dépôt officiel : de ce fait les instructions varient entre chaque distribution.
Ubuntu/Debian
La manière la plus idiomatique/correcte à mon sens est d’utiliser le module APT de cloud-init pour ajouter le dépôt officiel Docker.
Pour Ubuntu :
#cloud-config
apt:
sources:
docker:
source: "deb [signed-by=$KEY_FILE] https://download.docker.com/linux/ubuntu $RELEASE stable"
keyid: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88
packages:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
Remarque
Le paquet docker-ce dépend de containerd.io et de docker-ce-cli, qui recommandent eux-mêmes docker-buildx-plugin et docker-compose-plugin ; on devrait donc pouvoir s’en sortir en ne listant que docker-ce dans packages.
Mais par souci de fiabilité, les instructions officielles les listent tous, j’en fais donc de même ici.
Pour Debian c’est exactement la même chose, mais en remplaçant ubuntu par debian dans l’URL du dépôt source (https://download.docker.com/linux/debian).
Le module APT ne permettant pas de télécharger de clé GPG depuis une URL, cette méthode intègre directement l’empreinte de la clé du dépôt officiel Docker (keyid: 9DC8...) ; le jour où la clé changera, il faudra récupérer la nouvelle empreinte avec :
curl -s https://download.docker.com/linux/ubuntu/gpg \
| gpg --with-colons --show-keys \
| awk -F: '/^fpr/ { print $10; exit }'
Si curl/wget est disponible dans l’image, on peut contourner cette contrainte en téléchargeant la clé séparément :
#cloud-config
bootcmd:
- cloud-init-per once fetch_docker_gpg curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
apt:
sources:
docker:
source: "deb [signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $RELEASE stable"
packages:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
Précision
L’utilisation de bootcmd plutôt que du plus classique runcmd est nécessaire à cause de l’ordre d’exécution des modules cloud-init, qui fait que runcmd s’exécute quasiment en dernier et donc après apt et packages. Cela conduit à une erreur du fait de l’absence de la clé lors de l’ajout du dépôt :
[191744.376988] cloud-init[204]: Reading package lists...
[191744.386138] cloud-init[204]: W: OpenPGP signature verification failed: https://download.docker.com/linux/ubuntu resolute InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 7EA0A9C3F273FCD8
[191744.386184] cloud-init[204]: E: The repository 'https://download.docker.com/linux/ubuntu resolute InRelease' is not signed.
bootcmd s’exécute très tôt durant le lancement d’une instance, et permet ainsi de récupérer la clé avant la configuration du dépôt et le téléchargement des paquets.
cloud-init-per once s’assure que la commande soit exécutée uniquement une fois, autrement les bootcmsd sont exécutées à chaque démarrage.
Si curl n’est pas disponible dans l’image, on peut l’installer avant la récupération de la clé :
#cloud-config
bootcmd:
- cloud-init-per once install_curl apt-get -yq install curl
- cloud-init-per once fetch_docker_gpg curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
apt:
sources:
docker:
source: "deb [signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $RELEASE stable"
packages:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
Rocky/Alma
En attendant que le dépôt officiel Docker devienne fonctionnel pour ces distributions (ne retenez pas votre souffle, cela fait des années que c’est “en cours”…), pour Rocky c’est celui de RHEL qu’il faut ajouter selon cette doc (ce qui fait sens puisque Rocky est un rebuild de RHEL) et pour Alma celui de CentOS (Alma étant basée sur CentOS Stream).
Exemple pour Rocky
Docker fournit le fichier docker-ce.repo qui définit 4 dépôts dans le format compris par yum/dnf :
[docker-ce-stable]
name=Docker CE Stable - $basearch
baseurl=https://download.docker.com/linux/rhel/$releasever/$basearch/stable
enabled=1
gpgcheck=1
gpgkey=https://download.docker.com/linux/rhel/gpg
[docker-ce-stable-source]
name=Docker CE Stable - Sources
baseurl=https://download.docker.com/linux/rhel/$releasever/source/stable
enabled=0
gpgcheck=1
gpgkey=https://download.docker.com/linux/rhel/gpg
[docker-ce-test]
name=Docker CE Test - $basearch
baseurl=https://download.docker.com/linux/rhel/$releasever/$basearch/test
enabled=0
gpgcheck=1
gpgkey=https://download.docker.com/linux/rhel/gpg
[docker-ce-test-source]
name=Docker CE Test - Sources
baseurl=https://download.docker.com/linux/rhel/$releasever/source/test
enabled=0
gpgcheck=1
gpgkey=https://download.docker.com/linux/rhel/gpg
L’équivalent de la directive apt.sources pour les distributions de la famille de Red Hat est yum_repos, mais je n’ai pas trouvé comment la pointer directement vers ce fichier pour qu’elle en utilise le contenu.
Si le dépôt Stable vous suffit (et de toute façon, les trois autres dépôts sont désactivés via enabled=0 et les deux dépôts Sources pointent vers des URL inexistantes), voici comment implémenter sa configuration avec yum_repos :
#cloud-config
yum_repos:
docker-ce:
name: Docker CE Stable - $basearch
baseurl: https://download.docker.com/linux/rhel/$releasever/$basearch/stable
enabled: true
gpgcheck: true
gpgkey: https://download.docker.com/linux/rhel/gpg
packages:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
runcmd:
# RHEL et clones ne démarrent pas automatiquement les services
- systemctl enable --now docker
Personnellement cette approche me convient bien, mais il est aussi possible de transcrire les instructions officielles en runcmd :
#cloud-config
runcmd:
- dnf install -y dnf-plugins-core
- dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
- dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
- systemctl enable --now docker
Je suis moins amateur, mais il faut reconnaître que ça fonctionne tout aussi bien. Comme souvent il n’y a pas qu’une seule bonne manière de faire les choses en informatique.
Pour Alma Linux, remplacez rhel dans l’URL avec centos : https://download.docker.com/linux/rhel/docker-ce.repo.
Arch
En bonus et parce que ça me tient à cœur, voici comment procéder sur Arch Linux.
Il n’y a pas de dépôt officiel Docker pour Arch, et c’est bien normal puisque l’une des raisons d’être de ces dépôts est de fournir la version la plus à jour des paquets possible, ce qui est déjà le cas sur Arch.
De ce fait on peut simplement faire :
#cloud-config
packages:
- docker
- docker-buildx
- docker-compose
runcmd:
- systemctl --now enable docker.socket
Oui c’est vraiment simple : ce n’est pas surprenant puisque Arch le principe KISS (Keeps It Simple, Stupid) est au cœur de ses principes.
Bonus : ajouter l’utilisateur au groupe docker
… N’est pas recommandé pour des questions de sécurité, mais si jamais vous en avez besoin voici l’astuce que j’utilise pour ça fonctionne entre différentes distributions :
runcmd:
# Ajoute l'utilisateur avec UID 1000 au groupe docker
- [ sh, -c, "usermod -aG docker $(awk -F: '$3==1000{print $1}' /etc/passwd)" ]
Cela suppose que le premier utilisateur non-privilégié ait l’UID 1000 (et que ce soit celui que l’on veut ajouter au groupe docker), ce qui est commun mais pas garanti.
C’est nécessaire car chaque image cloud utilise un nom d’utilisateur par défaut différent (en général le nom de la distribution), et que l’on ne puisse pas utiliser la clé default ou default_user pour s’y substituer :
#cloud-config
groups:
- docker: default
conduit à l’erreur :
[24784.216271] cloud-init[201]: 2026-04-30 16:59:27,202 - distros[WARNING]: Unable to add group member 'default' to group 'docker'; user does not exist.
De plus, créer un groupe ainsi au lieu de laisser le paquet docker le créer lors de son installation est à éviter, notamment parce qu’il aura alors un GID ≥ 1000 alors que normalement les comptes système sont ≤ 999.
Je n’ai pas non plus connaissance d’une manière simple de dire “ajoute l’utilisateur par défaut au groupe docker” sans remplacer tous les autres groupes supplémentaires de l’utilisateur par défaut, donc ce que l’on voit parfois :
#cloud-config
users:
- name: ubuntu
groups: docker
Résulte en l’écrasement des groupes secondaires par défaut :
$ id
uid=1000(ubuntu) gid=1001(ubuntu) groups=1001(ubuntu),1000(docker)
Alors que normalement on a :
uid=1000(ubuntu) gid=1001(ubuntu) groups=1001(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),1000(lxd)
Au final, la méthode la plus simple et portable est de remplacer le nom de l’utilisateur par défaut et ensuite d’ajouter cet utilisateur au groupe via un runcmd tardif :
user:
name: cloud
runcmd:
- usermod -aG docker cloud
Exemple complet pour Ubuntu :
#cloud-config
apt:
sources:
docker:
source: "deb [signed-by=$KEY_FILE] https://download.docker.com/linux/ubuntu $RELEASE stable"
keyid: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88
packages:
- docker-ce
user:
name: cloud
runcmd:
- usermod -aG docker cloud
Bien sûr si vous n’utilisez qu’une ou deux distributions, vous pouvez alors intégrer le nom de l’utilisateur par défaut de cette distribution dans la config :
#cloud-config
apt:
sources:
docker:
source: "deb [signed-by=$KEY_FILE] https://download.docker.com/linux/ubuntu $RELEASE stable"
keyid: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88
packages:
- docker-ce
runcmd:
- usermod -aG docker ubuntu
Avec tout ceci j’espère que vous ne rencontrerez aucun souci pour lancer des instances avec le moteur Docker prêt à l’emploi !