Fixer le noyau au prochain démarrage de votre VPS
Sur une image cloud Ubuntu, GRUB_DEFAULT reste sans effet. Lisez les entrées réellement générées et fixez le prochain noyau sans risquer la console de secours.
Ce qui détermine le noyau utilisé au prochain démarrage de votre VPS
Le noyau utilisé au prochain démarrage de votre VPS est déterminé par un fichier généré, /boot/grub/grub.cfg. Vous ne modifiez jamais ce fichier directement. Vous modifiez ses sources, puis vous le régénérez. Sur une image cloud Ubuntu, l’une de ces sources provient du fournisseur de l’image. Elle 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 laptop.
Procédez dans cet ordre. Vérifiez que vous pouvez choisir le noyau. Lisez tous les fichiers sources, 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 ensuite une méthode de pinning. Une erreur sur une machine à laquelle vous accédez uniquement par SSH peut vous obliger à utiliser une console de secours. Les solutions les plus sûres se trouvent donc à la fin de cette page et sont souvent les bonnes.
Commencez par vérifier que le noyau vous appartient et peut être sélectionné
uname -r
systemd-detect-virt
ls -1 /boot/vmlinuz-*systemd-detect-virt affiche kvm, qemu ou xen : vous utilisez votre propre noyau et tout ce qui suit s’applique. Si lxc ou openvz s’affiche, votre serveur partage le noyau de l’hôte. Vous n’avez donc pas de bootloader à vous et aucun noyau à sélectionner. 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 et 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 pendant un autoremove. Il est préférable de comprendre ce mécanisme avant de supprimer les anciens noyaux sur Ubuntu d’un serveur auquel vous tenez.
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 est perdu lors de l’installation ou de la suppression du prochain paquet du noyau, car les scripts de ces paquets le régénèrent.
cat /usr/sbin/update-grubupdate-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/, puis écrit le résultat. Deux commandes, un seul sens : les entrées vont dedans et grub.cfg en sort.
Ce qui écrase votre configuration : /etc/default/grub.d
grep -rn '^[^#]' /etc/default/grub /etc/default/grub.d/Le deuxième chemin est souvent oublié. grub-mkconfig charge d’abord /etc/default/grub, puis chaque fichier *.cfg de /etc/default/grub.d/ selon l’ordre des motifs glob. Lisez le code qui effectue cette opération :
grep -n 'default/grub' /usr/sbin/grub-mkconfigLe chargement utilise un shell classique : la dernière affectation est donc prioritaire. 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 la lecture 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 ce résultat au lieu de vous fier à cette phrase.
La règle pratique est la suivante : placez vos propres paramètres dans un fichier qui arrive en dernier dans l’ordre de tri, 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 la sélection dans le menu inutile
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.cfgGRUB_FORCE_PARTUUID demande au générateur de trouver le système de fichiers racine à l’aide 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 second grep affiche le code qui utilise 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 importante ici : 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.cfgSi le nombre est égal à 1, aucune seconde entrée ne peut être sélectionnée. GRUB_DEFAULT=1 désigne donc une entrée inexistante. GRUB ne peut pas la résoudre et démarre la première entrée, qui correspond au nouveau noyau que vous cherchiez à éviter. grub-set-default n’aide pas 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 rétablir un menu complet, mettez le fichier du fournisseur de côté et 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 nombre passe de 1 à plusieurs, les entrées apparaissent une fois la contrainte supprimée. Rien n’a encore été écrit. Remettez le fichier en place si le second décompte ne vous semble pas correct, car le PARTUUID forcé permet à l’image de votre fournisseur de localiser son système de fichiers racine. Le supprimer fait basculer la machine vers le chemin de recherche standard. Prenez un snapshot avant d’exécuter réellement update-grub.
Si votre seul objectif est de démarrer malgré 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 contourner une seule mise à niveau présente plus de risques que le problème n’en vaut la peine.
Pourquoi les numéros d’entrée sont le mauvais choix pour le pinning
GRUB_DEFAULT accepte un numéro, un titre ou un identifiant. Les numéros comptent les entrées de premier niveau à partir de 0. Une entrée imbriquée utilise > comme séparateur. GRUB_DEFAULT="1>2" désigne donc l’entrée à l’index 2 dans le sous-menu à l’index 1.
Les index changent. 10_linux liste les kernels du plus récent au plus ancien. L’installation d’un kernel décale donc chaque entrée plus ancienne d’une position, tandis que la suppression d’un kernel les fait remonter. Votre 1>2 soigneusement défini continue de fonctionner après cette modification. Il désigne alors un autre kernel. Aucune erreur n’est signalée, aucun avertissement n’est affiché et vous ne le découvrez qu’après un reboot.
Les identifiants ne changent pas, car chacun contient la version du kernel. Affichez le vôtre :
sudo awk -F"'" '/menuentry_id_option/ {print $2, "==>", $4}' /boot/grub/grub.cfgIgnorez les premières lignes de la sortie. Elles correspondent à la variable définie dans l’en-tête. Ensuite, la partie gauche est le titre affiché au lecteur et la partie droite est l’identifiant transmis aux outils. Pour une entrée située dans un sous-menu, concaténez 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 sur le noyau précédent avec grub-reboot
Une sélection ponctuelle est la bonne solution 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 provoque un kernel panic ne sera pas réessayé au démarrage suivant. Vous disposez d’une seule tentative, puis la machine revient automatiquement à sa valeur par défaut habituelle.
Vérifiez d’abord que votre configuration générée lit bien cette variable :
sudo grep -n -B2 -A5 'next_entry' /boot/grub/grub.cfgVous 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. C’est le 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 listgrub-editenv list doit maintenant afficher une ligne next_entry= contenant exactement la valeur que vous avez passée. Ouvrez la console de votre fournisseur dans un onglet du navigateur, puis redémarrez et vérifiez le résultat.
sudo rebootuname -rSi uname -r affiche l’ancienne version, cela signifie que le verrouillage a fonctionné. Si la nouvelle version s’affiche, soit l’identifiant n’a pas été résolu, soit grubenv n’est pas lu. Dans tous les cas, la machine a démarré, ce qui est précisément l’intérêt du mode ponctuel.
Rendre le choix persistant avec GRUB_DEFAULT=saved
GRUB_DEFAULT=saved fait venir l’entrée par défaut de saved_entry dans grubenv. Vous définissez cette valeur avec grub-set-default. Elle reste valable après l’installation de 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.cfgLa dernière commande doit afficher set default="${saved_entry}". Si elle affiche set default="0", un fichier chargé après le vôtre a redéfini GRUB_DEFAULT avec 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 autre paramètre, qu’il est facile de confondre avec celui-ci. Il enregistre l’entrée que vous venez de démarrer comme nouvelle valeur par défaut. La valeur par défaut suit donc le dernier démarrage réussi. Sur un serveur, un redémarrage unattended peut ainsi modifier discrètement votre choix. Laissez ce paramètre désactivé, sauf si c’est le comportement recherché.
Un choix fixé par 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 le système revient à la première entrée. Bloquez donc également le package, ou empêchez la suppression automatique de ce kernel.
Afficher le menu dans la console du fournisseur
La sélection interactive nécessite que le menu soit affiché. Les images cloud le masquent. Ajoutez ces lignes dans le fichier dont le nom vient en dernier, puis exécutez sudo update-grub.
GRUB_TIMEOUT=10
GRUB_TIMEOUT_STYLE=menu
GRUB_RECORDFAIL_TIMEOUT=10GRUB_TIMEOUT_STYLE=hidden avec GRUB_TIMEOUT=0 n’affiche absolument rien. Une personne qui surveille la console voit alors immédiatement apparaître les messages du kernel et en déduit que le bootloader a été ignoré. GRUB_RECORDFAIL_TIMEOUT est le délai d’attente distinct utilisé après un boot qui n’est pas arrivé à son terme. Les images cloud le définissent également sur 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 propose une console série plutôt qu’une console graphique et que rien ne s’affiche, GRUB écrit sur un terminal que vous ne pouvez pas voir. 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’opération la plus risquée présentée ici. Il existe des solutions plus simples, qui règlent généralement le vrai problème.
Bloquer les paquets du kernel. Si l’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 showholdUtilisez les noms affichés par la première commande, car les cloud images installent souvent la variante virtual ou kvm plutôt que generic. Si le kernel plus récent est apparu lors d’un rafraîchissement de l’image et que vous pensez que la release elle-même a changé, ce n’est pas le cas : une point release regroupe dans un nouveau support d’installation les mises à jour que vous avez déjà et n’ajoute à un serveur déjà corrigé aucun paquet qui ne lui aurait pas été proposé plusieurs semaines plus tôt. 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 ce blocage comme une pause avec une date de fin, puis retirez-le 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, le live kernel patching sur un VPS répond mieux à ce besoin.
Créer un snapshot avant la mise à niveau. Un snapshot se restaure en quelques minutes, sans saisie dans la console et sans risque d’appliquer partiellement une modification du bootloader. Créez le snapshot, effectuez la mise à niveau, redémarrez, puis vérifiez. Si le nouveau kernel se comporte mal, revenez au snapshot : le chemin de démarrage est alors exactement celui d’origine.
Utiliser la console ou une rescue image pour une machine déjà arrêtée. Une fois que le serveur ne démarre plus, la configuration du bootloader n’est pas l’endroit où vous devez intervenir. Cette procédure de récupération est distincte : que faire lorsqu’un VPS ne redémarre pas après une mise à jour du kernel.
Ce qui peut échouer et le message que vous verrez
Votre modification de /boot/grub/grub.cfg a disparu. Un paquet du kernel a été installé ou supprimé, son script de maintenance s’est exécuté avec 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é déclenche un kernel panic avec VFS: Unable to mount root fs on unknown-block(0,0). L’entrée que vous avez épinglée pointe vers un kernel ou un initrd qui n’est plus présent sur le disque. Cela arrive 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 reboot censé la modifier. Vérifiez successivement trois points : 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 courant ? grub.cfg contient-il une ligne set default qui lit la variable que vous avez définie ? L’un de ces trois points explique systématiquement le problème.
Le menu est apparu seul après un crash. GRUB enregistre un boot échoué dans grubenv sous la forme recordfail=1, ce qui force l’affichage du menu au boot suivant afin qu’un opérateur puisse intervenir. Effacez cette valeur avec sudo grub-editenv /boot/grub/grubenv unset recordfail une fois la machine rétablie.
La seule phrase à retenir : le fichier que vous modifiez n’est pas celui que GRUB lit, et sur une image cloud, c’est l’écart entre les deux qui crée la confusion. Commencez par lire la configuration générée. Toutes les décisions de cette page découlent de ce qu’elle contient réellement.
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 rien et GRUB revient à la première entrée. 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 le fournisseur 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 déclenche 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.
Dois-je utiliser le numéro de l’entrée ou son identifiant ?
Utilisez l’identifiant. Les numéros d’entrée correspondent à des positions dans une liste que `10_linux reconstruit du noyau le plus récent au plus ancien. L’installation ou la suppression d’un noyau suffit donc à les décaler. Un 1>2 obsolète peut toujours correspondre à une entrée réelle, mais incorrecte, sans afficher d’avertissement. Les identifiants contiennent la version du noyau. Ils correspondent donc au noyau voulu ou ne correspondent à aucune entrée. Listez-les avec sudo grep -n menuentry_id_option /boot/grub/grub.cfg`, puis copiez la chaîne entre guillemets qui suit chaque ligne d’entrée.
Est-il plus sûr de bloquer 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 ne risquez pas de faire une erreur 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 contrôlez le blocage avec apt-mark showhold. En contrepartie, un noyau bloqué ne reçoit plus les correctifs de sécurité. Décidez donc quand vous exécuterez sudo apt-mark unhold` avant d’activer le blocage.