Cloud-init automatise la configuration de vos VMs cloud au premier démarrage. Vous définissez vos utilisateurs, clés SSH, paquets et scripts dans un fichier YAML appelé user-data, et cloud-init les applique sans intervention manuelle.
Ce guide vous apprend à créer un user-data fonctionnel, à le tester localement avec Incus, et à déboguer quand ça ne marche pas. Vous comprendrez aussi une distinction cruciale : user-data ≠ configuration réseau.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Écrire un fichier user-data (utilisateurs, paquets, fichiers, commandes)
- Situer chaque action dans les 5 boot stages de cloud-init
- Valider le fichier avant déploiement, puis le tester dans un conteneur Incus
- Déboguer un déploiement avec les logs et
cloud-init status - Éviter les pièges courants (réseau, secrets, idempotence)
Prérequis : les bases de l'administration Linux (fichiers, paquets, utilisateurs), et la leçon précédente, Images, cloud-init et service de métadonnées, qui explique ce qu'est une image et pourquoi le user-data ne doit jamais contenir de secret. Pour l'exercice pratique, il faut un hôte Linux avec Incus installé.
Ce que cloud-init fait (et ne fait pas)
Section intitulée « Ce que cloud-init fait (et ne fait pas) »Cloud-init est un outil d'initialisation, pas de gestion de configuration continue : il ne repasse pas régulièrement remettre une machine en conformité, contrairement à Ansible ou Puppet.
La nuance compte, parce que la formule « cloud-init, c'est le premier démarrage » est répandue et inexacte. Le service participe à chaque démarrage, et ce sont ses modules qui portent une fréquence : per-instance pour la majorité, ce qui explique l'impression du premier démarrage, mais aussi per-once, per-boot et always pour ceux qui doivent rejouer. Concrètement, l'essentiel de ce que vous écrivez dans les données utilisateur s'applique une fois par instance, tandis qu'un module déclaré per-boot rejoue à chaque redémarrage. Le détail des fréquences et la façon de forcer un rejeu sont traités plus bas.
Il sait notamment :
- Créer des utilisateurs avec leurs clés SSH et droits sudo
- Installer des paquets via apt, yum, dnf ou zypper
- Écrire des fichiers de configuration sur le disque
- Exécuter des commandes à la fin de l'initialisation
- Configurer le hostname et d'autres paramètres système
En revanche, cloud-init ne peut pas modifier la configuration réseau via user-data dans la plupart des cas, c'est un point qui crée beaucoup de confusion.
Les 5 boot stages officiels
Section intitulée « Les 5 boot stages officiels »Cloud-init organise son exécution en 5 stages distincts. Comprendre cette séquence vous aidera à déboguer quand vos scripts ne s'exécutent pas au bon moment.
1. Detect (ds-identify)
Section intitulée « 1. Detect (ds-identify) »Ce premier stage détermine si cloud-init doit s'exécuter et quel datasource utiliser. Le script ds-identify cherche des indices (fichiers, métadonnées) pour identifier l'environnement cloud (AWS, GCP, Azure, OpenStack, NoCloud...).
Si aucun datasource n'est détecté, cloud-init peut être désactivé automatiquement. C'est pourquoi sur certaines images "non-cloud", cloud-init ne fait rien.
2. Local (avant le réseau)
Section intitulée « 2. Local (avant le réseau) »Ce stage s'exécute avant que le réseau ne soit configuré. Cloud-init applique la configuration réseau (depuis le datasource, pas depuis user-data) et exécute les commandes bootcmd si présentes.
C'est ici que se joue la configuration des interfaces réseau. À la fin de ce stage, le réseau devrait être opérationnel.
3. Network (user-data disponible)
Section intitulée « 3. Network (user-data disponible) »Une fois le réseau actif, cloud-init récupère les données depuis le datasource : metadata, vendor-data et user-data. Les modules "cloud_init_modules" s'exécutent, ils préparent le terrain pour la configuration principale.
4. Config (la configuration principale)
Section intitulée « 4. Config (la configuration principale) »C'est le cœur de l'action. Cloud-init exécute les modules "cloud_config_modules" qui appliquent votre user-data :
- Création des utilisateurs et groupes
- Configuration SSH
- Installation des paquets (
packages:) - Écriture de fichiers (
write_files:)
5. Final (scripts utilisateur)
Section intitulée « 5. Final (scripts utilisateur) »Le dernier stage exécute les modules "cloud_final_modules", notamment :
- runcmd : vos commandes personnalisées
- scripts-user : scripts dans
/var/lib/cloud/scripts/ - phone-home : notification vers un serveur externe
Vos commandes runcmd ne s'exécutent donc qu'à ce stage, après les paquets et les fichiers. Si un runcmd ne trouve pas encore ce qu'il attend, vérifiez d'abord que le stage Final est terminé.
Anatomie d'un fichier user-data
Section intitulée « Anatomie d'un fichier user-data »Un fichier user-data commence obligatoirement par #cloud-config (sans espace) sur la première ligne. Le reste est du YAML standard.
#cloud-config
# Utilisateurs à créerusers: - name: deploy groups: sudo shell: /bin/bash ssh_authorized_keys: - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... sudo: ALL=(ALL) NOPASSWD:ALL
# Mise à jour des paquets existantspackage_update: truepackage_upgrade: true
# Paquets à installerpackages: - nginx - git - htop
# Fichiers à créerwrite_files: - path: /etc/motd content: | Bienvenue sur ce serveur configuré par cloud-init! permissions: '0644'
# Commandes à exécuter (stage Final)runcmd: - systemctl enable nginx - systemctl start nginxLes directives les plus utiles
Section intitulée « Les directives les plus utiles »Voici les directives que vous utiliserez dans la plupart des cas :
- users crée des utilisateurs avec leurs clés SSH. Chaque utilisateur peut avoir
groups,shell,ssh_authorized_keysetsudo. - packages liste les paquets à installer. Cloud-init détecte automatiquement le gestionnaire de paquets (apt, yum, dnf...).
- package_update et package_upgrade mettent à jour la liste des paquets et les paquets installés avant d'en installer de nouveaux.
- write_files crée des fichiers avec un contenu spécifique. Utile pour déposer des configurations.
- runcmd exécute des commandes shell après que tout le reste soit configuré. Attention : ces commandes s'exécutent en tant que root.
Valider votre configuration avant déploiement
Section intitulée « Valider votre configuration avant déploiement »Ne déployez jamais un user-data sans l'avoir validé. Cloud-init fournit une commande de validation qui détecte les erreurs de syntaxe YAML et les directives inconnues.
Installer cloud-init localement
Section intitulée « Installer cloud-init localement »Pour valider vos fichiers user-data avant de les déployer, installez cloud-init avec pipx (installation isolée, sans polluer votre système) :
# Installer pipx si nécessaire# Linux: sudo apt install pipx && pipx ensurepath# macOS: brew install pipx && pipx ensurepath
pipx install cloud-initVérifiez l'installation :
cloud-init --versioncloud-init schema -c user-data.yaml --annotateLa commande cloud-init schema -c fichier.yaml --annotate fonctionne sans
sudo et ne nécessite pas que cloud-init soit "actif" sur le système. C'est
parfait pour valider vos fichiers depuis votre poste de développement. Cette
installation avec pipx permet de valider la syntaxe, mais pas d'exécuter
cloud-init complètement. Pour tester l'exécution réelle, utilisez Incus
ou Multipass (voir section "Tester avec Incus").
Validation d'un fichier local
Section intitulée « Validation d'un fichier local »cloud-init schema -c user-data.yaml --annotateLe flag --annotate ajoute des commentaires utiles en cas d'erreur, indiquant exactement quelle ligne pose problème.
Si tout va bien :
Valid schema user-data.yamlSi erreur :
user-data.yaml:12: 'package' is not a valid keyword (did you mean 'packages'?)Validation sur une instance en cours d'exécution
Section intitulée « Validation sur une instance en cours d'exécution »Pour vérifier que l'instance a bien reçu et interprété votre user-data :
sudo cloud-init schema --system --annotateCette commande valide le user-data tel qu'il a été reçu par l'instance, utile pour déboguer les problèmes de transmission.
Tester avec Incus (sandbox locale)
Section intitulée « Tester avec Incus (sandbox locale) »Incus (fork de LXD) est parfait pour tester vos configurations cloud-init sans consommer de ressources cloud. Les conteneurs Incus avec images /cloud supportent nativement cloud-init.
Une instance Incus n'a aucune adresse publique, ce qui reproduit le cas d'une machine cloud créée sans adresse publique : elle reçoit une adresse privée du pont géré par Incus, et sa sortie vers Internet passe par la traduction d'adresses (NAT) de ce pont. C'est ce chemin qui permet à package_update et packages de télécharger leurs paquets. Le détail, avec les équivalents Scaleway et OUTSCALE, est dans la leçon Images, cloud-init et service de métadonnées.
Lister les images cloud disponibles
Section intitulée « Lister les images cloud disponibles »incus image list images: architecture=x86_64 /cloud -c lat | head -20Vous verrez des images comme ubuntu/noble/cloud, debian/bookworm/cloud, almalinux/9/cloud, ce sont celles qui incluent cloud-init.
Lancer un conteneur avec user-data
Section intitulée « Lancer un conteneur avec user-data »-
Créez votre fichier user-data
Enregistrez ce contenu dans
user-data.yaml. La première ligne du fichier doit être exactement#cloud-config: toute ligne placée avant, même le nom du fichier en commentaire, rend le fichier invalide et cloud-init l'ignore. La règlesudo: ALL=(ALL) NOPASSWD:ALLdonne les droits root sans mot de passe : elle n'est acceptable que dans ce conteneur jetable de test, jamais sur une machine durable.user-data.yaml #cloud-configusers:- name: testusergroups: sudoshell: /bin/bashssh_authorized_keys:- ssh-ed25519 AAAAC3...votre-clé...sudo: ALL=(ALL) NOPASSWD:ALLpackage_update: truepackages:- curl- vimruncmd:- touch /tmp/cloud-init-complete -
Lancez le conteneur avec le user-data
Fenêtre de terminal incus launch images:ubuntu/noble/cloud test-cloud-init \--config=cloud-init.user-data="$(cat user-data.yaml)"Le
\en fin de ligne poursuit la commande sur la ligne suivante : n'ajoutez aucun espace après lui. -
Attendez que cloud-init termine
Fenêtre de terminal incus exec test-cloud-init -- cloud-init status --waitRésultat attendu :
status: done -
Vérifiez que tout est configuré
Fenêtre de terminal # L'utilisateur existe ?incus exec test-cloud-init -- id testuser# Les paquets sont installés ?incus exec test-cloud-init -- which curl# Le fichier témoin existe ?incus exec test-cloud-init -- ls /tmp/cloud-init-complete
Utiliser un profil Incus pour réutiliser la config
Section intitulée « Utiliser un profil Incus pour réutiliser la config »Si vous testez souvent, créez un profil plutôt que de passer le user-data à chaque fois :
# Créer le profilincus profile create cloud-init-test
# Éditer pour ajouter le user-dataincus profile edit cloud-init-testAjoutez dans le YAML du profil :
config: cloud-init.user-data: | #cloud-config users: - name: deploy groups: sudo shell: /bin/bash sudo: ALL=(ALL) NOPASSWD:ALL package_update: true packages: - nginxPuis lancez avec le profil :
incus launch images:ubuntu/noble/cloud mon-test \ --profile default --profile cloud-init-testAjouter une configuration réseau (NoCloud)
Section intitulée « Ajouter une configuration réseau (NoCloud) »Si vous avez besoin de configurer le réseau (IP statique par exemple), utilisez cloud-init.network-config :
incus config set mon-conteneur cloud-init.network-config - << 'EOF'version: 2ethernets: eth0: dhcp4: false addresses: - 10.0.0.50/24 routes: - to: default via: 10.0.0.1 nameservers: addresses: - 8.8.8.8EOFPièges courants
Section intitulée « Pièges courants »Ces erreurs reviennent constamment et produisent des VMs « à moitié configurées ». Les connaître évite des heures de diagnostic au premier boot.
Mettre la configuration réseau dans user-data
Section intitulée « Mettre la configuration réseau dans user-data »runcmd: - ip addr add 10.0.0.50/24 dev eth0Symptôme : le réseau ne se configure pas, ou se réinitialise au boot suivant.
Cause : user-data ne peut pas configurer le réseau, c'est une source de données séparée. Passez par cloud-init.network-config (Incus) ou par le metadata service du provider.
Oublier #cloud-config
Section intitulée « Oublier #cloud-config »users: - name: deploySymptôme : le fichier est ignoré, aucune configuration n'est appliquée.
Cause : sans #cloud-config en première ligne, cloud-init ne reconnaît pas le format.
#cloud-configusers: - name: deployAttendre runcmd trop tôt
Section intitulée « Attendre runcmd trop tôt »Vous cherchez un fichier créé par runcmd juste après le boot.
Symptôme : le fichier n'existe pas encore.
Cause : runcmd s'exécute au stage Final, après packages et write_files. Attendez avec cloud-init status --wait, ou consultez /var/log/cloud-init-output.log.
Scripts non idempotents avec cloud-init clean
Section intitulée « Scripts non idempotents avec cloud-init clean »Un script est idempotent quand le rejouer ne change pas le résultat. Or cloud-init clean suivi d'un redémarrage rejoue tout le user-data.
runcmd: - useradd deploySymptôme : erreur user already exists après un cloud-init clean suivi d'un reboot.
Cause : useradd échoue si l'utilisateur existe déjà. Préférez la directive users:, qui est idempotente.
users: - name: deploy groups: sudoSecrets dans user-data
Section intitulée « Secrets dans user-data »write_files: - path: /etc/app/secrets.env content: API_KEY=sk-xxxSymptôme : les secrets sont exposés via le metadata service, lisible par toute la VM, et restent écrits en clair sur le disque (voir la leçon Images, cloud-init et service de métadonnées).
Cause : user-data est accessible via http://169.254.169.254/latest/user-data (chez AWS ; l'adresse du service de métadonnées varie selon le fournisseur). Utilisez un gestionnaire de secrets, ou récupérez les secrets au moment de l'exécution plutôt qu'à la création.
Indentation YAML incorrecte
Section intitulée « Indentation YAML incorrecte »packages:- nginx - gitSymptôme : erreur de parsing ou comportement inattendu.
Cause : YAML est strict sur l'indentation, des espaces, jamais de tabulations. Validez systématiquement avec cloud-init schema -c fichier.yaml --annotate.
Confondre package et packages
Section intitulée « Confondre package et packages »package: - nginxSymptôme : la directive est ignorée, les paquets ne sont pas installés.
Cause : la directive correcte est packages, avec un « s ».
packages: - nginxDéboguer quand ça ne marche pas
Section intitulée « Déboguer quand ça ne marche pas »Cloud-init génère des logs détaillés. Voici comment les exploiter efficacement.
Vérifier l'état global
Section intitulée « Vérifier l'état global »cloud-init status --longSortie typique :
status: doneextended_status: doneboot_status_code: enabled-by-generatorlast_update: Wed, 29 Jan 2026 10:15:42 +0000detail: DataSourceNoCloud [seed=/var/lib/cloud/seed/nocloud-net]errors: []recoverable_errors: {}Les champs importants :
- status :
running(en cours),done(terminé),error(échec) - detail : le datasource utilisé
- errors : liste des erreurs bloquantes
Consulter les logs
Section intitulée « Consulter les logs »Les deux fichiers de log principaux :
# Log détaillé de chaque actionsudo less /var/log/cloud-init.log
# Sortie des scripts (stdout/stderr)sudo less /var/log/cloud-init-output.logPour chercher des erreurs rapidement :
sudo grep -i error /var/log/cloud-init.logsudo grep -i warn /var/log/cloud-init.logAnalyser les performances
Section intitulée « Analyser les performances »Si cloud-init est lent, identifiez les étapes qui prennent du temps :
cloud-init analyze blameSortie exemple :
-- Boot Record 01 -- 45.23s (init-network/config-apt-configure) 12.45s (init-network/config-package-update-upgrade-install) 3.21s (modules-config/config-ssh)Ici, la mise à jour des paquets prend 45 secondes, c'est souvent le cas sur les premières exécutions.
Forcer une ré-exécution (debug uniquement)
Section intitulée « Forcer une ré-exécution (debug uniquement) »Comme expliqué plus haut, vos données utilisateur ne s'appliquent qu'une fois par instance, car cloud-init mémorise ce qu'il a fait. Pour lui faire oublier cet état et forcer un rejeu complet en phase de mise au point :
# Supprimer les sémaphores (cloud-init "oublie" qu'il a déjà tourné)sudo cloud-init clean
# Au prochain reboot, cloud-init rejouera toutsudo rebootVous pouvez aussi exécuter un module spécifique sans rebooter. Pour rejouer les commandes runcmd, il faut lancer scripts_user, car cc_runcmd ne fait qu'écrire le script :
sudo cloud-init single --name scripts_user --frequency alwaysLa plupart des modules ne s'exécutent qu'une fois par instance : sans l'option --frequency always, cloud-init peut considérer le module comme déjà passé et ne rien faire. Pour rejouer un autre module, remplacez scripts_user par son nom (write_files, par exemple).
Cas d'usage avancé : Ansible en mode pull
Section intitulée « Cas d'usage avancé : Ansible en mode pull »Cloud-init peut installer Ansible et exécuter un playbook depuis un dépôt Git via ansible-pull. C'est utile pour des configurations plus complexes que ce que cloud-init gère nativement.
#cloud-configusers: - name: ansible groups: sudo shell: /bin/bash sudo: ALL=(ALL) NOPASSWD:ALL
package_update: truepackages: - git - python3-pip
write_files: - path: /home/ansible/.config/pip/pip.conf content: | [global] break-system-packages = true owner: ansible:ansible
ansible: install_method: pip package_name: ansible run_user: ansible pull: url: https://github.com/votre-org/playbooks.git playbook_name: site.ymlCette configuration :
- Crée un utilisateur
ansibleavec sudo sans mot de passe - Installe Ansible via pip
- Clone le repo Git et exécute
site.yml
Comprendre vendor-data
Section intitulée « Comprendre vendor-data »En plus de user-data (que vous fournissez), cloud-init consomme deux autres sources de données :
metadata contient des informations sur l'instance (ID, région, adresse IP...) fournies par le provider cloud.
vendor-data est une configuration fournie par le provider cloud ou l'image. Elle s'applique avant votre user-data et peut installer des agents, configurer des services par défaut, etc.
Qui fournit quoi ?
Section intitulée « Qui fournit quoi ? »Ce tableau permet de savoir qui est responsable de chaque source de données lue par cloud-init : le provider cloud, l'image ou vous. Lisez-le ligne par ligne pour repérer ce que vous pouvez modifier (le user-data) et ce que vous subissez.
| Source | Fourni par | Exemple |
|---|---|---|
| metadata | Provider cloud | Instance ID, IP privée |
| vendor-data | Provider / image | Agent de monitoring, config réseau |
| user-data | Vous | Users, packages, scripts |
| network-config | Provider / datasource | IP statique, routes |
Désactiver des parties de vendor-data
Section intitulée « Désactiver des parties de vendor-data »Si le vendor-data du provider interfère avec votre config, vous pouvez le filtrer :
#cloud-configvendordata: excluded: - text/part-handlerOù lire la documentation de référence ?
Section intitulée « Où lire la documentation de référence ? »La documentation officielle de cloud-init est la source qui fait foi quand une directive ou un comportement change d'une version à l'autre. Ces cinq pages sont celles qui servent le plus après cette leçon.
- Documentation cloud-init, référence complète
- Boot stages explained, les 5 stages en détail
- Network configuration, pourquoi user-data ≠ réseau
- NoCloud datasource, pour les tests locaux
- Incus cloud-init, intégration Incus
Mettre en pratique
Section intitulée « Mettre en pratique »Le lab fait naître la première machine de la boutique sous Incus :
vous complétez un user-data (la démarche de la section « Tester avec
Incus ») qui installe et démarre son service, sur un
réseau privé préparé par le lab, sans adresse publique. dsoxlab vérifie
l'état de la machine, pas vos commandes : cloud-init terminé sans erreur,
service actif, aucune adresse joignable depuis l'extérieur, puis machine et
réseau détruits à la fin de l'essai.
À retenir
Section intitulée « À retenir »Cloud-init est puissant mais a des règles précises. Gardez ces points en tête :
Le réseau n'est pas dans user-data. C'est la confusion #1. La config réseau vient du datasource ou de network-config séparé.
La configuration des données utilisateur s'applique une fois par instance, alors que cloud-init tourne à chaque démarrage : un module déclaré per-boot ou always rejoue, les autres non. Pour tout rejouer en mise au point, cloud-init clean puis redémarrage, en gardant à l'esprit que vos scripts doivent être idempotents.
Cloud-init a 5 stages. Detect → Local → Network → Config → Final. runcmd tourne au stage Final, après que le réseau et les paquets soient prêts.
Validez toujours avant de déployer. cloud-init schema -c file.yaml --annotate vous évite des heures de debug.
Incus est parfait pour tester. Utilisez les images /cloud et séparez user-data et network-config.
Les logs sont vos amis. /var/log/cloud-init.log et cloud-init status --long répondent à la plupart des questions.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : Réseau cloud : de l'adressage au filtrage par défaut
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications