Installer un serveur Ubuntu dans une machine virtuelle prend dix minutes de clics — et le résultat n'est jamais tout à fait le même d'une fois sur l'autre. L'installateur d'Ubuntu Server sait pourtant se configurer tout seul à partir d'un fichier. Voici la méthode complète, de la machine vide au serveur joignable en SSH, sans jamais toucher un écran d'installation.
Le principe
L'installateur d'Ubuntu Server, subiquity, accepte un fichier YAML décrivant tous les choix que l'on ferait à la main : disque, réseau, compte, paquets. Ce fichier lui est transmis par le mécanisme cloud-init NoCloud, c'est-à-dire une petite image ISO contenant deux fichiers et portant un nom de volume précis : CIDATA.
La machine virtuelle démarre donc avec deux lecteurs de CD : l'ISO d'installation d'Ubuntu, et cette ISO « seed » de quelques centaines de kilo-octets. Un argument passé au noyau — autoinstall — indique à l'installateur de ne poser aucune question.
Étape 1 — Préparer l'hôte
sudo apt install -y qemu-kvm libvirt-daemon-system virtinst xorriso acl sudo usermod -aG libvirt,kvm "$USER" # puis se déconnecter/reconnecter
Vérifier que la virtualisation matérielle est active et que libvirt répond :
grep -Eoc '(vmx|svm)' /proc/cpuinfo # doit renvoyer un nombre > 0 virsh -c qemu:///system net-list # le réseau "default" doit être actif
Le réseau default de libvirt fournit un sous-réseau NAT en 192.168.122.0/24, avec la passerelle 192.168.122.1. C'est celui utilisé dans la suite.
Étape 2 — Écrire le fichier user-data
C'est le cœur de la méthode. Créez un répertoire de travail contenant user-data :
#cloud-config
autoinstall:
version: 1
locale: fr_FR.UTF-8
keyboard:
layout: fr
timezone: Europe/Paris
# Réseau : adresse IP fixe. Le "match" évite de dépendre du nom exact
# de l'interface (enp1s0, ens3...).
network:
version: 2
ethernets:
primaire:
match:
name: "en*"
dhcp4: false
addresses:
- 192.168.122.50/24
routes:
- to: default
via: 192.168.122.1
nameservers:
addresses: [192.168.122.1, 1.1.1.1]
# Disque entier, partitionnement simple (sans LVM)
storage:
layout:
name: direct
identity:
hostname: mon-serveur
username: admin
# Hash du mot de passe : openssl passwd -6 'monmotdepasse'
password: "$6$exemple$RemplacezCeHashParLeVotre"
ssh:
install-server: true
allow-pw: true
authorized-keys:
- "ssh-ed25519 AAAA... ma-cle"
packages:
- qemu-guest-agent
- curl
- vim
updates: security
late-commands:
# sudo sans mot de passe : pratique pour le provisionnement automatisé
- echo 'admin ALL=(ALL) NOPASSWD:ALL' > /target/etc/sudoers.d/90-admin
- chmod 0440 /target/etc/sudoers.d/90-admin
# témoin de fin d'installation, utile pour scripter la suite
- curtin in-target --target=/target -- sh -c 'echo ok > /etc/install-stamp'
shutdown: reboot
Deux points méritent l'attention :
- le mot de passe se calcule avec openssl passwd -6 'monmotdepasse' — l'installateur refuse un mot de passe en clair ;
- les late-commands s'exécutent dans le système fraîchement installé, monté sous /target. C'est l'endroit où poser tout ce qui doit exister au premier démarrage.
Étape 3 — Le fichier meta-data
Deux lignes suffisent, mais le fichier doit exister :
instance-id: mon-serveur-001 local-hostname: mon-serveur
Étape 4 — Fabriquer l'ISO « seed »
xorriso -as mkisofs -V CIDATA -J -r -o seed.iso user-data meta-data
Le nom de volume CIDATA n'est pas décoratif : c'est exactement ce que cloud-init recherche sur les périphériques de la machine. Une faute de frappe ici, et l'installateur attendra sagement une saisie humaine.
Étape 5 — Créer le disque et lancer l'installation
qemu-img create -f qcow2 mon-serveur.qcow2 40G virt-install \ --connect qemu:///system \ --name mon-serveur \ --memory 4096 --vcpus 2 \ --cpu host-passthrough --machine q35 \ --osinfo name=ubuntu24.04 \ --disk path=$PWD/mon-serveur.qcow2,format=qcow2,bus=virtio \ --disk path=$PWD/seed.iso,device=cdrom,readonly=on \ --network network=default,model=virtio,mac=52:54:00:11:22:33 \ --graphics vnc,listen=127.0.0.1 \ --console pty,target_type=serial \ --location /chemin/ubuntu-24.04-live-server-amd64.iso,kernel=casper/vmlinuz,initrd=casper/initrd \ --extra-args "autoinstall console=ttyS0,115200n8" \ --noautoconsole --wait -1
Chaque option a sa raison d'être :
- --location ...,kernel=casper/vmlinuz,initrd=casper/initrd extrait le noyau de l'ISO afin de pouvoir lui passer un argument. Sans cela, impossible d'ajouter autoinstall ;
- --extra-args "autoinstall" supprime la demande de confirmation de l'installateur ;
- le second --disk ...,device=cdrom présente l'ISO seed à cloud-init ;
- une adresse MAC fixe permet ensuite de réserver l'adresse IP côté hyperviseur ;
- --wait -1 rend la main quand l'installation est terminée, ce qui permet d'enchaîner sur un script de provisionnement.
Pour figer l'adresse au niveau de libvirt et éviter qu'une autre machine ne la prenne :
virsh -c qemu:///system net-update default add ip-dhcp-host \ "<host mac='52:54:00:11:22:33' name='mon-serveur' ip='192.168.122.50'/>" \ --live --config
Étape 6 — Suivre l'installation
Rien ne s'affiche dans le terminal : c'est normal. Trois moyens d'observer ce qui se passe :
virsh -c qemu:///system console mon-serveur # console série, Ctrl+] pour sortir virsh -c qemu:///system screenshot mon-serveur /tmp/ecran.png virsh -c qemu:///system domstate mon-serveur
Comptez dix à quinze minutes selon la machine : extraction du système, installation des paquets demandés, mises à jour de sécurité, puis redémarrage automatique sur le disque.

L'installation se déroule sans personne devant l'écran : la console de la machine virtuelle est le seul endroit où l'on peut suivre l'avancement.
Étape 7 — Vérifier
ssh admin@192.168.122.50 ip -4 addr show # 192.168.122.50/24 cat /etc/install-stamp # ok sudo -n true && echo "sudo sans mot de passe opérationnel"
À partir de là, la machine est prête à recevoir un script de provisionnement — installation d'un service, d'un cluster, de ce que vous voulez — puisque l'accès SSH par clé et l'élévation de privilèges non interactive sont déjà en place.
Les pièges qui font perdre une soirée
- Le nom de volume CIDATA. Mal orthographié ou absent, cloud-init ne trouve rien et l'installateur affiche son premier écran, en attente d'un humain.
- L'argument noyau oublié. Sans autoinstall, selon les versions, l'installateur demande encore une confirmation avant d'écrire sur le disque.
- Le nom de l'interface réseau. Il varie selon le type de machine émulée. Utiliser match: name: "en*" plutôt qu'un nom en dur évite un serveur installé mais injoignable.
- Les droits d'accès aux fichiers. Si le disque et les ISO sont rangés sous /home, le processus QEMU (qui tourne sous un compte dédié) ne peut pas les lire. La parade tient en une ligne : setfacl -m u:libvirt-qemu:rx sur les répertoires, rw sur le disque.
- Le profil matériel inconnu. Si --osinfo refuse une version trop récente, indiquer la version LTS précédente ne pose aucun problème : ce paramètre ne sert qu'à choisir des réglages matériels par défaut.
- La source d'installation. Sur les images récentes, la source par défaut empile plusieurs couches de système de fichiers. Sur certaines combinaisons de noyau, la copie se fige définitivement — installation bloquée, aucune écriture disque, aucun message. Le contournement consiste à demander explicitement la variante à image unique :
source: id: ubuntu-server-minimal search_drivers: false
Elle contient moins de paquets, ce qui est plutôt un avantage sur un serveur, et les paquets utiles se rajoutent par la clé packages.
Industrialiser la méthode
Une fois la recette au point, il ne reste plus qu'à l'enfermer dans un script : génération de l'ISO seed, suppression d'une éventuelle machine du même nom, création du disque, pose des droits, réservation de l'adresse, lancement de l'installation. Une commande, une machine neuve, toujours identique.
C'est là que se trouve le vrai gain : non pas les dix minutes de clics économisées, mais la capacité de reconstruire à l'identique — après une erreur, pour tester une modification, ou pour monter trois machines au lieu d'une en changeant deux variables.
Variante utile à connaître : pour des machines jetables et créées en série, l'image « cloud » officielle d'Ubuntu, associée au même fichier cloud-init, démarre en une trentaine de secondes sans passer par un installateur. L'ISO reste préférable quand on veut maîtriser précisément le partitionnement ou partir d'un média déjà présent sur le réseau.