SSD Nodes Learn 🎉 VPS dès $5.50/mois
Guides Matt ConnorPar Matt Connor

Comment choisir le noyau au prochain démarrage d’un VPS

Sur une image cloud Ubuntu, GRUB_DEFAULT reste sans effet. Lisez les entrées réelles de GRUB et verrouillez le prochain noyau sans perdre l’accès SSH.

Ce qui détermine le noyau utilisé au prochain démarrage de votre VPS

Le noyau utilisé par votre VPS au prochain démarrage est déterminé par un seul fichier généré, /boot/grub/grub.cfg. Vous ne modifiez jamais ce fichier directement. Vous modifiez ses fichiers source, puis vous le régénérez. Sur une image cloud Ubuntu, l’un de ces fichiers source provient du fournisseur de l’image. Il peut rendre la sélection dans le menu sans effet. C’est pourquoi GRUB_DEFAULT=1 suivi de update-grub ne change rien sur un serveur loué, alors que ces deux étapes fonctionnent sur une installation de bureau ou de portable.

Suivez cet ordre. Vérifiez que vous pouvez choisir le noyau. Lisez tous les fichiers source, y compris ceux ajoutés par le fournisseur. Lisez le fichier généré et comptez les entrées qu’il contient réellement. Choisissez seulement ensuite une méthode de pinning. Une erreur sur une machine à laquelle vous accédez uniquement via SSH peut vous obliger à utiliser une console de secours. Les méthodes les plus sûres sont donc présentées à la fin de cette page et sont souvent les bonnes.

Vérifiez d’abord que vous pouvez configurer votre propre noyau

uname -r
systemd-detect-virt
ls -1 /boot/vmlinuz-*

systemd-detect-virt affiche kvm, qemu ou xen si vous utilisez votre propre noyau et que toute la suite s’applique. Si la commande affiche lxc ou openvz, votre serveur partage le noyau de l’hôte. Vous ne disposez donc pas de votre propre bootloader et vous ne pouvez rien configurer. Dans ce cas, uname -r affiche une version qui n’apparaît pas du tout dans /boot/vmlinuz-*, car le noyau en cours d’exécution appartient à l’hôte. Aucun paramètre enregistré sur votre disque ne peut le modifier.

ls -1 /boot/vmlinuz-* contient la liste réelle des noyaux que vous pouvez sélectionner. Si cette liste ne contient qu’une ligne, le noyau précédent a déjà été supprimé. Aucun paramètre du bootloader ne peut le restaurer. Cela se produit généralement lors d’un autoremove. Il est préférable de comprendre ce mécanisme avant de supprimer les anciens noyaux sur Ubuntu d’un serveur important.

Le fichier que vous modifiez n’est pas celui que GRUB lit

/etc/default/grub contient de simples affectations de variables shell. C’est une entrée. /boot/grub/grub.cfg est la sortie. Il commence par # DO NOT EDIT THIS FILE et la raison. Tout ce que vous écrivez dans le fichier de sortie disparaît lors de l’installation ou de la suppression suivante d’un paquet du kernel, car les scripts de ces paquets le régénèrent.

cat /usr/sbin/update-grub

update-grub est un wrapper. Il exécute grub-mkconfig -o /boot/grub/grub.cfg, qui lit les variables, exécute chaque script de /etc/grub.d/ et écrit le résultat. Deux commandes, un seul sens : les entrées sont fournies en entrée et grub.cfg est généré.

Ce qui remplace votre configuration : /etc/default/grub.d

grep -rn '^[^#]' /etc/default/grub /etc/default/grub.d/

Le deuxième chemin est la partie souvent oubliée. grub-mkconfig charge d’abord /etc/default/grub, puis chaque fichier *.cfg dans /etc/default/grub.d/, selon l’ordre de classement des motifs glob. Consultez le code qui effectue cette opération :

grep -n 'default/grub' /usr/sbin/grub-mkconfig

Le chargement des fichiers utilise un shell standard : la dernière affectation l’emporte. Les images cloud Ubuntu fournissent des fichiers dans ce répertoire. Ces fichiers définissent notamment le délai d’attente et la ligne de commande du kernel après le chargement de votre fichier. Votre GRUB_TIMEOUT=10 dans /etc/default/grub est écrasé quelques instants plus tard par un fichier du fournisseur qui le définit à 0. La commande grep ci-dessus affiche les affectations exactes présentes sur votre image. Consultez donc ces résultats au lieu de vous fier à cette phrase.

La règle pratique est la suivante : placez vos propres paramètres dans un fichier qui sera chargé en dernier, par exemple /etc/default/grub.d/99-local.cfg, au lieu de modifier /etc/default/grub. Ainsi, aucun fichier fourni par l’image ne pourra être chargé après le vôtre.

Pourquoi GRUB_FORCE_PARTUUID rend le choix dans le menu sans effet

grep -rn GRUB_FORCE_PARTUUID /etc/default/grub /etc/default/grub.d/
grep -n GRUB_FORCE_PARTUUID /etc/grub.d/10_linux
sudo grep -n 'root=PARTUUID' /boot/grub/grub.cfg

GRUB_FORCE_PARTUUID indique au générateur de trouver le système de fichiers racine à partir de l’UUID de partition, directement inscrit sur la ligne de commande du noyau sous la forme root=PARTUUID=..., au lieu de rechercher un UUID de système de fichiers pendant le démarrage. Le fournisseur de l’image définit cette variable pour qu’une même image disque démarre de manière fiable sur du matériel différent de celui utilisé pour sa création. Le deuxième grep affiche le code qui exploite cette variable, dans /etc/grub.d/10_linux. Ce script se trouve sur votre propre disque. C’est lui qui fait autorité pour déterminer le comportement de votre image.

La conséquence est le point important : dans ce chemin d’exécution, le générateur écrit une entrée de démarrage directe au lieu de générer la liste complète des noyaux installés. Comptez le nombre d’entrées obtenues.

sudo grep -cE '^\s*(menuentry|submenu) ' /boot/grub/grub.cfg
sudo grep -nE '^\s*(menuentry|submenu) ' /boot/grub/grub.cfg

Si le compte est égal à 1, aucune deuxième entrée n’est disponible. GRUB_DEFAULT=1 désigne donc une entrée qui n’existe pas. GRUB ne peut pas la résoudre et démarre la première entrée, c’est-à-dire le nouveau noyau que vous cherchiez à éviter. grub-set-default ne change rien non plus, car le problème ne vient pas de l’entrée par défaut. Le menu que vous essayez de sélectionner n’a jamais été généré.

Pour retrouver un menu complet, déplacez le fichier du fournisseur, puis prévisualisez le résultat avant de l’appliquer. grub-mkconfig sans -o écrit sur la sortie standard et ne modifie rien sur le disque.

sudo grub-mkconfig 2>/dev/null | grep -cE '^\s*(menuentry|submenu) '
sudo mkdir -p /root/grub-backup
sudo mv /etc/default/grub.d/<the file your grep named> /root/grub-backup/
sudo grub-mkconfig 2>/dev/null | grep -cE '^\s*(menuentry|submenu) '

Si le compte passe de 1 à plusieurs entrées, celles-ci apparaissent une fois que le forçage est supprimé. Rien n’a encore été écrit. Remettez le fichier en place si le deuxième compte ne vous paraît pas correct, car le PARTUUID forcé permet à l’image de votre fournisseur de localiser le système de fichiers racine. Sa suppression fait passer la machine par le chemin de recherche à la place. Créez un snapshot avant d’exécuter réellement update-grub.

Si votre seul objectif est de survivre à un noyau défectueux, arrêtez-vous ici et utilisez les options plus sûres présentées plus loin. Reconstruire le menu de démarrage d’un serveur distant pour éviter une seule mise à niveau présente plus de risques que le problème n’en vaut la peine.

Pourquoi les index des entrées sont le mauvais choix pour un verrouillage

GRUB_DEFAULT accepte un nombre, un titre ou un identifiant. Les nombres comptent les entrées de premier niveau à partir de 0. Une entrée imbriquée utilise > comme séparateur. Ainsi, GRUB_DEFAULT="1>2" désigne l’entrée d’index 2 dans le sous-menu d’index 1.

Les index changent. 10_linux liste les noyaux du plus récent au plus ancien. L’installation d’un noyau décale donc chaque entrée plus ancienne d’une position, tandis que la suppression d’un noyau les remonte. Votre valeur 1>2 reste valide après cette modification. Elle désigne alors un autre noyau. Aucune erreur n’est générée et aucun avertissement n’est affiché. Vous le découvrez après un redémarrage.

Les identifiants ne changent pas, car chacun contient la version du noyau. Affichez le vôtre :

sudo awk -F"'" '/menuentry_id_option/ {print $2, "==>", $4}' /boot/grub/grub.cfg

Ignorez les premières lignes de la sortie. Elles correspondent à la variable définie dans l’en-tête. Ensuite, le côté gauche contient le titre affiché à l’utilisateur et le côté droit contient l’identifiant transmis aux outils. Pour une entrée située dans un sous-menu, assemblez l’identifiant du sous-menu et celui de l’entrée avec >, dans cet ordre, exactement comme dans la forme numérique.

Démarrer une fois avec le noyau précédent avec grub-reboot

Une sélection valable pour un seul démarrage est le bon choix sur un serveur distant, car elle s’annule automatiquement. grub-reboot écrit next_entry dans /boot/grub/grubenv. GRUB lit cette variable, l’efface, puis enregistre la valeur effacée avant de démarrer quoi que ce soit. Ainsi, un noyau qui déclenche un kernel panic n’est pas réessayé au démarrage suivant. Vous disposez d’une seule tentative, puis la machine revient automatiquement à sa valeur par défaut habituelle.

Commencez par vérifier que votre configuration générée lit bien cette variable :

sudo grep -n -B2 -A5 'next_entry' /boot/grub/grub.cfg

Vous devez trouver une ligne load_env et un bloc qui définit default à partir de next_entry. Si grep n’affiche rien, votre image ne lit jamais grubenv au démarrage. grub-reboot sera donc accepté par le shell, puis ignoré par le bootloader. Il s’agit du même chemin de démarrage direct forcé que dans la section précédente, mais à un autre endroit.

sudo grub-reboot '<the identifier you copied>'
sudo grub-editenv list

grub-editenv list doit maintenant afficher une ligne next_entry= contenant exactement la valeur que vous avez fournie. Ouvrez la console de votre fournisseur dans un onglet du navigateur, puis redémarrez et vérifiez le résultat.

sudo reboot
uname -r

uname -r qui affiche l’ancienne version signifie que le verrouillage a fonctionné. Si l’ancienne version affiche la nouvelle version, cela signifie soit que l’identifiant n’a pas été résolu, soit que grubenv n’est pas lu. Dans les deux cas, la machine est démarrée, ce qui est précisément l’intérêt de la forme à usage unique.

Rendre le choix persistant avec GRUB_DEFAULT=saved

GRUB_DEFAULT=saved fait venir l’entrée par défaut de saved_entry dans grubenv, et vous définissez cette valeur avec grub-set-default. Elle reste valable après l’installation de nouveaux kernels, car update-grub réécrit grub.cfg sans jamais modifier grubenv.

echo 'GRUB_DEFAULT=saved' | sudo tee /etc/default/grub.d/99-local.cfg
sudo update-grub
sudo grub-set-default '<the identifier you copied>'
sudo grub-editenv list
sudo grep -n 'set default' /boot/grub/grub.cfg

La dernière commande doit afficher set default="${saved_entry}". Si elle affiche set default="0", un fichier chargé après le vôtre a rétabli GRUB_DEFAULT à une valeur littérale. Affichez donc de nouveau /etc/default/grub.d/ et vérifiez que 99-local.cfg est bien traité en dernier.

GRUB_SAVEDEFAULT=true est un paramètre différent, que l’on confond facilement avec celui-ci. Il enregistre ce que vous venez de démarrer comme nouvelle entrée par défaut. L’entrée par défaut suit donc le dernier démarrage réussi. Sur un serveur, un redémarrage sans intervention peut ainsi modifier discrètement votre choix. Laissez ce paramètre désactivé, sauf si c’est le comportement souhaité.

Un choix basé sur un identifiant peut tout de même échouer dans un cas. Si vous supprimez le kernel désigné, l’identifiant ne correspond plus à aucune entrée et GRUB revient à la première entrée. Conservez donc également le package, ou empêchez la suppression automatique de ce kernel.

Afficher le menu sur la console du fournisseur

Le choix interactif nécessite que le menu soit affiché à l’écran, mais les images cloud le masquent. Ajoutez ces lignes dans le fichier qui est chargé en dernier, puis exécutez sudo update-grub.

GRUB_TIMEOUT=10
GRUB_TIMEOUT_STYLE=menu
GRUB_RECORDFAIL_TIMEOUT=10

GRUB_TIMEOUT_STYLE=hidden associé à GRUB_TIMEOUT=0 n’affiche absolument rien. Une personne qui surveille la console voit donc les messages du kernel apparaître immédiatement et en conclut que le bootloader a été ignoré. GRUB_RECORDFAIL_TIMEOUT correspond au délai d’attente distinct utilisé après un boot qui n’est pas arrivé à son terme. Les images cloud le définissent également à 0. C’est pourquoi un serveur qui vient d’échouer au boot ne s’arrête toujours pas pour vous laisser intervenir.

Si votre fournisseur fournit une console série plutôt qu’une console graphique et que vous ne voyez toujours rien, GRUB écrit vers un terminal auquel vous n’avez pas accès. Ajoutez les deux lignes ensemble : la première sélectionne les sorties et la seconde configure le port :

GRUB_TERMINAL="console serial"
GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1"

Dix secondes sont désormais ajoutées à chaque boot. Rétablissez le délai à 0 lorsque vous avez terminé.

Options plus sûres que la modification du bootloader

Modifier les paramètres d’entrée du bootloader sur une machine à laquelle vous accédez uniquement via SSH est l’option la plus risquée de cette page. Il existe des solutions plus simples, qui corrigent généralement le véritable problème.

Bloquer les paquets du kernel. Si votre objectif est de « ne pas installer un kernel plus récent », indiquez-le au gestionnaire de paquets plutôt qu’au bootloader.

apt list --installed 2>/dev/null | grep -E '^linux-(image|headers|generic|virtual|kvm|aws|azure|gcp|oracle)'
sudo apt-mark hold linux-image-virtual linux-headers-virtual
apt-mark showhold

Utilisez les noms affichés par la première commande, car les images cloud installent souvent la variante virtual ou kvm plutôt que generic. Un paquet bloqué est ignoré par apt upgrade, qui l’indique avec The following packages have been kept back:, et il est également ignoré par les mises à niveau automatiques sur Ubuntu. Le coût est réel : un kernel bloqué ne reçoit plus les correctifs de sécurité. Considérez donc cette mesure comme une pause avec une date de fin, puis réactivez le paquet avec sudo apt-mark unhold. Si vous évitez les mises à jour du kernel parce que les redémarrages entraînent une interruption de service, et non parce qu’un kernel pose problème, l’application de correctifs live au kernel sur un VPS répond à ce besoin.

Créer un snapshot avant la mise à niveau. Un snapshot se restaure en quelques minutes, sans saisie via la console et sans risque de modification partiellement appliquée du bootloader. Créez le snapshot, effectuez la mise à niveau, redémarrez, puis vérifiez le système. Si le nouveau kernel pose problème, restaurez le snapshot : le chemin de démarrage sera exactement celui d’origine.

Utiliser la console ou une image de secours pour une machine déjà arrêtée. Une fois que le serveur ne démarre plus, ce n’est pas dans la configuration du bootloader que vous devez intervenir. Cette procédure de récupération est distincte : que faire lorsqu’un VPS ne démarre plus après une mise à jour du kernel.

Ce qui peut échouer et le message affiché

Votre modification de /boot/grub/grub.cfg a disparu. Un paquet du kernel a été installé ou supprimé, son script de maintenance a exécuté update-grub, puis le fichier a été régénéré à partir des sources. L’en-tête # DO NOT EDIT THIS FILE indique les deux emplacements sources. Modifiez ces fichiers.

grub-editenv: error: environment block too small. /boot/grub/grubenv est absent ou tronqué. Recréez-le avec sudo grub-editenv /boot/grub/grubenv create, définissez de nouveau votre valeur, puis vérifiez-la avec sudo grub-editenv list.

Un kernel épinglé provoque un kernel panic avec VFS: Unable to mount root fs on unknown-block(0,0). L’entrée épinglée pointe vers un kernel ou un initrd qui n’est plus présent sur le disque. Cela se produit généralement parce que le paquet a été supprimé alors que l’identifiant est resté dans grubenv. Pour récupérer le système, démarrez depuis la console sur une entrée fonctionnelle, puis effacez la valeur obsolète.

uname -r n’a pas changé après un redémarrage censé la modifier. Vérifiez trois éléments dans l’ordre : grub-editenv list affiche-t-il toujours votre valeur, ou a-t-elle été consommée ; l’identifiant que vous avez défini apparaît-il dans le grub.cfg actuel ; grub.cfg contient-il une ligne set default qui lit la variable définie. L’un de ces trois éléments explique toujours le problème.

Le menu est apparu après un crash sans intervention de votre part. GRUB enregistre un démarrage échoué dans grubenv sous la forme recordfail=1. Cela force l’affichage du menu au démarrage suivant afin qu’un opérateur puisse intervenir. Effacez cette valeur avec sudo grub-editenv /boot/grub/grubenv unset recordfail une fois la machine à nouveau opérationnelle.


La phrase à retenir est la suivante : le fichier que vous modifiez n’est pas celui que GRUB lit. Sur une image cloud, c’est dans cet écart que se trouve la confusion. Consultez d’abord la configuration générée. Toutes les décisions présentées ici découlent de son contenu réel.

FAQ

Pourquoi GRUB_DEFAULT=1 ne change-t-il pas le noyau sur lequel mon VPS démarre ?

Sur une image cloud Ubuntu, le fichier `/boot/grub/grub.cfg généré ne contient souvent qu’une seule entrée de démarrage. L’index 1 ne désigne donc aucune entrée et GRUB revient à la première. Vérifiez-le avec sudo grep -cE '^\s*(menuentry|submenu) ' /boot/grub/grub.cfg. Un total de 1 confirme le problème. La cause est GRUB_FORCE_PARTUUID, défini par l’éditeur de l’image dans un fichier situé sous /etc/default/grub.d/. Ce paramètre force le générateur à utiliser un démarrage direct au lieu de construire une liste complète des noyaux installés. Recherchez le fichier avec grep -rn GRUB_FORCE_PARTUUID /etc/default/grub /etc/default/grub.d/`.

Comment démarrer une seule fois sur le noyau précédent ?

Exécutez `sudo grub-reboot '<identifier>' avec un identifiant copié depuis votre propre grub.cfg, puis redémarrez en laissant la console du fournisseur ouverte. GRUB efface next_entry avant le démarrage. Le choix ne s’applique donc qu’à une seule tentative et un noyau qui provoque un kernel panic ne sera pas réessayé. Vérifiez que la valeur a bien été enregistrée avec sudo grub-editenv list. Avant de vous y fier, exécutez sudo grep -n next_entry /boot/grub/grub.cfg, car une image dont la configuration ne charge jamais grubenv` ignorera la commande sans afficher d’erreur.

Vaut-il mieux verrouiller le noyau par numéro d’entrée ou par identifiant ?

Par identifiant. Les numéros d’entrée correspondent à des positions dans une liste que `10_linux reconstruit en plaçant les versions les plus récentes en premier. L’installation ou la suppression d’un noyau déplace donc ces numéros. Un 1>2 obsolète peut encore désigner une entrée existante mais incorrecte, sans afficher d’avertissement. Les identifiants contiennent la version du noyau. Ils correspondent donc au noyau souhaité ou ne correspondent à aucune entrée. Listez-les avec sudo grep -n menuentry_id_option /boot/grub/grub.cfg` et copiez la chaîne entre guillemets qui suit chaque ligne d’entrée.

Est-il plus sûr de conserver le paquet du noyau que de modifier le bootloader ?

Pour l’objectif habituel, oui. `sudo apt-mark hold linux-image-virtual linux-headers-virtual empêche l’installation d’un noyau plus récent. Le chemin de démarrage ne change donc jamais et vous n’avez rien à modifier depuis une console à laquelle vous n’aurez peut-être pas accès. Vérifiez d’abord les noms des variantes installées sur votre serveur avec apt list --installed, puis confirmez le verrouillage avec apt-mark showhold. En contrepartie, un noyau verrouillé ne reçoit aucun correctif de sécurité. Décidez donc quand vous exécuterez sudo apt-mark unhold` avant d’appliquer le verrouillage.