HardStacked
Partager :

cloud-init · netplan

Wi-Fi headless sans Imager sur Raspberry Pi OS Trixie : wpa_supplicant.conf est ignoré depuis Bookworm (et la radio reste bloquée)

Publié le · HardStacked · 9 min

Tu as écrit l'image Raspberry Pi OS sur la carte avec dd. Tu as mis un wpa_supplicant.conf dans la partition boot, un fichier vide nommé ssh à côté, et tu as branché un Pi Zero 2 W sans écran ni clavier. Rien n'apparaît sur ton réseau. La documentation officielle dit que le truc du wpa_supplicant.conf « isn't available from Raspberry Pi OS Bookworm and onwards » (n'est plus disponible depuis Bookworm) et te renvoie vers Imager. La réponse la plus votée sur Stack Exchange te renvoie vers custom.toml. Aucune ne dit quoi mettre sur la carte avec Trixie.

Quelqu'un l'a demandé sur Raspberry Pi Stack Exchange le 2026-02-11 (question 154041, Pi Zero 2 W, Trixie, 270 vues, aucune réponse) :

« How can I connect to a WIFI network at boot so I can ssh to the thing and configure it? » (Comment me connecter à un réseau Wi-Fi au démarrage pour faire du ssh dessus et le configurer ?)

La question plus ancienne 144850, « Headless Wi-Fi setup without the RPi Imager: bootfs/wpa_supplicant.conf not supported since Bookworm » (2023-11-02), a 18 227 vues et aucune réponse acceptée. Les deux ont été lues par l'API de Stack Exchange le 2026-10-20.

01. Le verdict

Sur Trixie, le Wi-Fi du premier démarrage vient de cloud-init. Le fichier s'appelle network-config (format netplan) et il est dans la partition boot. Il est déjà sur ta carte, entièrement en commentaire. wpa_supplicant.conf n'est lu par rien, et rien ne te le dit.

Il y a un deuxième piège derrière le premier. La radio Wi-Fi naît bloquée (rfkill). Sur notre banc, le seul chemin qui l'a débloquée est celui de cloud-init. Répare le fichier sans la radio, ou la radio sans le fichier, et tu restes hors ligne.

02. Le banc

Mesuré le 2026-10-20 sur un Pi Zero 2 W. Image officielle 2026-10-06-raspios-trixie-arm64-lite (sha256 vérifié), écrite avec dd sur toute la carte à chaque fois : chaque passe part d'une image fraîchement écrite. Journal rendu persistant dès le premier démarrage pour pouvoir le lire après. Deux passes, une seule différence : la passe 1 a un network-config comme l'écrit Imager, la passe 2 a l'ancien wpa_supplicant.conf (et le network-config d'origine, entièrement en commentaire, identique à celui de l'image, vérifié avec cmp).

Même image, même user-data, même cmdline.txt (pays CA), même réseau, même mot de passe. Le Pi démarre les deux fois : le premier démarrage se termine et cloud-init applique le hostname.

Passe 1 : network-configPasse 2 : wpa_supplicant.conf
fichier Wi-Fi luoui, un profil NetworkManager est créé0 mention dans le journal, rien copié dans /etc, 0 profil
lignes « unblock set for type wifi »90
radiodébloquée« Wi-Fi disabled by radio killswitch; disabled by state file »
wlan0activatedreste unavailable

La passe 1 a été refaite une deuxième fois, sur la même carte réécrite de zéro : même résultat, encore 9 lignes unblock. Les comptes de la passe 2 ont été calculés avec grep sur le journal sauvegardé, lu depuis la carte sur un autre poste, pas tapés sur le Pi.

03. Premier voyant : le fichier est ignoré en silence

Nous avons lu les deux images officielles (Trixie et Bookworm, même date) sans les démarrer. Un grep de wpa_supplicant.conf dans etc, usr/lib, usr/bin et usr/sbin des deux systèmes de fichiers racine retourne un seul fichier, le même dans les deux : /usr/lib/raspi-config/init_resize.sh. Aucune cmdline.txt ne l'appelle :

cmdline.txt livrée : Trixie, puis Bookworm
console=serial0,115200 console=tty1 root=PARTUUID=27b94958-02 rootfstype=ext4 fsck.repair=yes rootwait resize console=serial0,115200 console=tty1 root=PARTUUID=9d05ddda-02 rootfstype=ext4 fsck.repair=yes rootwait quiet init=/usr/lib/raspberrypi-sys-mods/firstboot

Première ligne Trixie, deuxième Bookworm. Puis la passe 2 l'a confirmé en vrai : le fichier reste dans la partition boot, intact, et le journal n'en parle jamais.

La documentation dit que la fonction a disparu, en une phrase (raspberrypi/documentation, commit ddea98e, 2026-10-13, getting-started/setting-up.adoc, ligne 405). Elle ne nomme ni cloud-init ni network-config. Chercher cloud-init, network-config ou custom.toml dans configuration/ et getting-started/ retourne 0 fichier.

04. Deuxième voyant : la radio naît bloquée

L'image livre /etc/modprobe.d/rfkill_default.conf, identique sur Bookworm et Trixie :

Trixie, sur le Pi
$ cat /etc/modprobe.d/rfkill_default.conf
options rfkill default_state=0

Toutes les radios démarrent en blocage logiciel. Le seul avertissement que nous avons trouvé dans l'image est /etc/profile.d/wifi-check.sh. Deux choses le rendent facile à rater. Il s'exécute à l'ouverture d'une session, donc sur un Pi headless personne ne le voit jamais. Et il se tait dès que cfg80211.ieee80211_regdom= est dans cmdline.txt :

/etc/profile.d/wifi-check.sh, extrait
if grep -s -q 'cfg80211\.ieee80211_regdom=' /boot/firmware/cmdline.txt; then exit 0 fi

En passe 2, ce paramètre était dans cmdline.txt, et la radio est restée bloquée quand même. Selon la logique du script, l'avertissement ne se serait pas affiché, mais nous n'avions aucune session console ouverte : cette partie est déduite du script, pas observée.

La passe 1 montre l'autre sens. Au premier démarrage, le journal enregistre « unblock set for type wifi » neuf fois. La première arrive à 25,5 secondes, dans l'exécution du premier démarrage de cloud-init (cloud-init-main.service), les huit autres entre 26 et 53 secondes. Nous n'avons pas établi quel maillon de la chaîne le demande, donc nous n'en nommons aucun. Ce qu'on peut dire : la chaîne du premier démarrage de cloud-init débloque la radio quand network-config déclare un Wi-Fi, et en passe 2, où network-config n'en déclarait aucun, rien ne l'a fait. Les deux premières lignes de la passe 1, copiées du journal (journalctl -o short-monotonic) :

journal, passe 1, deux premières lignes
[ 25.568420] raspberrypi rfkill[832]: unblock set for type wifi [ 26.451132] raspberrypi rfkill[906]: unblock set for type wifi

Donc un tuto qui dit « remets wpa_supplicant.conf » règle un problème sur deux, et un tuto qui dit « fais rfkill unblock » règle l'autre.

05. Deux autres voyants

Le user-data livré sur la carte dit qu'il met en place « an initial user called "ubuntu" with password "ubuntu" ». C'est du texte générique de cloud-init. Le /etc/cloud/cloud.cfg de l'image crée un utilisateur pi avec lock_passwd: True :

/etc/cloud/cloud.cfg, lignes 76 à 78
default_user: name: pi lock_passwd: True

(lignes 76 à 78 du fichier)

Aucun mot de passe utilisable. Sans ton propre utilisateur et ta clé SSH dans user-data, tu ne te connecteras pas. (Depuis avril 2022 il n'y a plus d'utilisateur par défaut utilisable, comme le rappelle une réponse de la question 144850, qui renvoie à l'annonce Raspberry Pi d'avril 2022.)

Et la réponse la plus votée, custom.toml, passait par firstboot et init_config du paquet raspberrypi-sys-mods. L'image Bookworm les a (version 20250930~bookworm). L'image Trixie (1:20260914) n'a ni l'un ni l'autre, et sa cmdline.txt n'a plus d'init=. Nous l'avons lu dans l'image sans la démarrer : nous disons « rien dans l'image ne le lit plus », pas « nous l'avons vu échouer ». La réponse elle-même disait en 2023 que la fonction était expérimentale et « will be replaced by cloud-init in the future » (sera remplacée par cloud-init).

06. La méthode qui marche

Ces commandes sont fournies telles quelles, sans garantie. Teste d'abord sur une machine non critique ou sur un instantané.

Testée deux fois (passe 1, puis la même carte réécrite de zéro). Écris l'image, et vérifie d'abord le nom du périphérique avec lsblk, parce que dd ne pose pas deux fois la question :

écrire l'image
xz -dc 2026-10-06-raspios-trixie-arm64-lite.img.xz | sudo dd of=/dev/sdX bs=4M conv=fsync status=progress

Monte la partition boot (la FAT) et remplace network-config et user-data par ce qui suit. Change seulement les valeurs d'exemple.

network-config, partition boot
network: version: 2 wifis: wlan0: dhcp4: true regulatory-domain: "CA" access-points: "MyWiFi": password: "correct horse battery staple" optional: true
user-data, partition boot
#cloud-config manage_resolv_conf: false hostname: pizero manage_etc_hosts: true user: name: admin shell: /bin/bash lock_passwd: true ssh_authorized_keys: - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...yourkey you@laptop" sudo: ALL=(ALL) NOPASSWD:ALL ssh_pwauth: false runcmd: - [ systemctl, enable, --now, ssh ]

Ajoute ensuite ceci à la fin de l'unique ligne de cmdline.txt (même ligne, une espace avant). Imager fait pareil quand tu règles un pays ; nous n'avons pas testé sans :

fin de la ligne de cmdline.txt
cfg80211.ieee80211_regdom=CA

Le YAML veut des espaces, jamais de tabulations (le fichier livré le dit lui-même). Le nom du réseau va entre guillemets. regulatory-domain est ton code de pays. Pour SSH, notre test l'active avec la ligne runcmd. Un fichier vide nommé ssh dans la partition boot devrait aussi marcher : sshswitch.service est activé dans l'image et le lit. Nous ne l'avons pas testé.

Imager actuel écrit ce même format : password: au niveau du point d'accès, regulatory-domain, le pays dans cmdline.txt (rpi-imager, commit 4cf6217, 2026-10-20 : le network-config est construit dans src/customization_generator.cpp, et src/imagewriter.cpp ajoute cfg80211.ieee80211_regdom= à cmdline.txt dès qu'un pays est réglé, avec ou sans nom de réseau). Il ajoute un bloc ethernets: eth0 que notre test n'avait pas.

07. Vérifie

Après le premier démarrage, sur le Pi (cloud-init 25.2) :

après le premier démarrage, sur le Pi
$ cloud-init status
status: done
$ journalctl -b -o cat | grep -c "unblock set for type wifi"
9
$ /usr/sbin/rfkill list wlan
1: phy0: Wireless LAN Soft blocked: no Hard blocked: no
$ /usr/sbin/iw reg get | head -2
global country CA: DFS-FCC

Et le côté NetworkManager (quelques lignes coupées) :

côté NetworkManager, quelques lignes coupées
$ journalctl -b -o cat | grep -E "rfkill" | head -12
<info> [1791246501.5789] rfkill1: found Wi-Fi radio killswitch (at /sys/devices/platform/soc/3f300000.mmcnr/mmc_host/mmc1/mmc1:0001/mmc1:0001:1/ieee80211/phy0/rfkill1) (driver brcmfmac) <info> [1791246502.1493] manager: rfkill: Wi-Fi enabled by radio killswitch; enabled by state file

Les chemins complets de rfkill et iw ne sont pas un choix de style : dans notre session SSH sur le Pi, le PATH n'avait pas /usr/sbin, donc rfkill et iw tout court ont répondu « command not found », alors que les deux paquets sont installés.

08. WPA2, WPA3

La ligne password: est un raccourci netplan qui veut dire WPA-PSK avec PMF optionnel (netplan 1.1.2, src/parse.c). NetworkManager 1.52.1 traduit wpa-psk en mode de transition : il ajoute SAE (WPA3) quand le Wi-Fi le permet. Son propre commentaire de code dit « key-mgmt=wpa-psk doesn't strictly mean WPA1/WPA2 only, but also allows WPA3 (SAE) » (nm-supplicant-config.c).

Maintenant ce que nous avons vu. Sur un réseau WPA3 seul avec PMF obligatoire, notre Zero 2 W ne s'est jamais connecté. Les journaux que nous avons pu lire viennent des démarrages où nous avions forcé le profil en SAE : trois fois « Authentication timed out », puis NetworkManager a conclu à un mauvais mot de passe (« no-secrets »), alors que le mot de passe était bon. Sur un réseau WPA2, il s'est connecté du premier coup. Nous n'avons pas séparé SAE de PMF obligatoire. Le conseil pratique : si ton réseau est en WPA3 seul, teste le Zero 2 W sur un réseau WPA2 ou en transition avant de décider que ton mot de passe est faux.

09. Le mot de passe est en clair

network-config vit sur une partition FAT que n'importe qui tenant la carte peut lire, et cloud-init ne l'efface pas. Après le premier démarrage :

partition boot après le premier démarrage
$ ls /boot/firmware | grep -E "network-config|user-data|meta-data|wpa_supplicant|^ssh"
meta-data network-config user-data

Le mot de passe a aussi été recopié dans /var/log/cloud-init.log sur notre carte. Retire-le des deux une fois le Pi en ligne. Le sudo NOPASSWD du user-data ci-dessus est le défaut de l'utilisateur pi de l'image : durcis-le si le Pi reste en service.

10. Ce que nous avons mesuré, et ce que non

Mesuré : un Pi Zero 2 W, image Trixie Lite arm64 du 2026-10-06, cloud-init 25.2, NetworkManager 1.52.1, netplan 1.1.2. Lu sans démarrer : l'image Bookworm du même jour. Non mesuré : Pi 4 ou 5, un Bookworm démarré, l'image Desktop, un réseau caché, l'Ethernet.

La même forme traverse le reste de cette série, où le système déclare une chose et en fait une autre : un timedatectl qui affiche NTP service: active alors que l'horloge n'a jamais reçu l'heure, un update-ca-certificates qui affiche 0 added, 0 removed que ton certificat soit pris ou ignoré, et un systemctl restart sshd qui dit que l'unité n'existe pas pendant que SSH continue de répondre.

Partager :