Fijar el kernel que arrancará tu VPS la próxima vez
GRUB_DEFAULT no funciona en una imagen cloud de Ubuntu. Comprueba las entradas reales de GRUB y fija el próximo arranque sin perder el acceso SSH.
Qué decide el kernel con el que arranca tu VPS
El kernel con el que arrancará tu VPS la próxima vez lo decide un único archivo generado: /boot/grub/grub.cfg. Nunca edites ese archivo. Edita sus entradas y vuelve a generarlo. En una imagen de Ubuntu para la nube, una de esas entradas procede del proveedor de la imagen y puede hacer que la selección del menú sea irrelevante. Por eso GRUB_DEFAULT=1 seguido de update-grub no cambia nada en un servidor alquilado, mientras que los mismos dos pasos funcionan en una instalación en un portátil.
Sigue este orden. Confirma que puedes elegir el kernel. Lee todos los archivos de entrada, incluidos los que haya añadido el proveedor. Lee la salida generada y cuenta las entradas que contiene realmente. Sólo después elige un método de fijación. Si te equivocas en una máquina a la que sólo accedes mediante SSH, necesitarás una consola de rescate. Por eso, las respuestas más seguras aparecen al final de esta página y a menudo son las adecuadas.
Primero compruebe que puede seleccionar su kernel
uname -r
systemd-detect-virt
ls -1 /boot/vmlinuz-*systemd-detect-virt que muestra kvm, qemu o xen significa que ejecuta su propio kernel y que todo lo siguiente es aplicable. lxc o openvz significa que el servidor comparte el kernel del host. Por tanto, no tiene un bootloader propio ni ningún kernel que pueda seleccionar. En ese caso, uname -r muestra una versión que no aparece en /boot/vmlinuz-*, porque el kernel en ejecución pertenece al host y ninguna configuración de su disco puede cambiarlo.
ls -1 /boot/vmlinuz-* es la lista real de kernels entre los que puede elegir. Si contiene una sola línea, el kernel anterior ya se ha eliminado y ninguna configuración del bootloader puede recuperarlo. Esto suele ocurrir durante un autoremove. Conviene entenderlo antes de eliminar kernels antiguos en Ubuntu de un servidor importante.
El archivo que edita no es el archivo que lee GRUB
/etc/default/grub contiene asignaciones simples de variables de shell. Es una entrada. /boot/grub/grub.cfg es la salida, y comienza con # DO NOT EDIT THIS FILE y el motivo. Cualquier cambio que escriba en la salida desaparece la próxima vez que se instale o elimine un paquete del kernel, porque los scripts de esos paquetes la vuelven a generar.
cat /usr/sbin/update-grubupdate-grub es un envoltorio. Ejecuta grub-mkconfig -o /boot/grub/grub.cfg, que lee las variables, ejecuta cada script de /etc/grub.d/ y escribe el resultado. Son dos comandos en una sola dirección: las entradas entran y grub.cfg sale.
Qué sobrescribe la configuración: /etc/default/grub.d
grep -rn '^[^#]' /etc/default/grub /etc/default/grub.d/La segunda ruta es la parte que suele pasarse por alto. grub-mkconfig obtiene primero /etc/default/grub y después cada archivo *.cfg de /etc/default/grub.d/ en el orden de expansión glob. Consulte el código que lo hace:
grep -n 'default/grub' /usr/sbin/grub-mkconfigLa carga se realiza mediante el shell estándar, por lo que prevalece la última asignación. Las imágenes cloud de Ubuntu incluyen archivos en ese directorio y establecen valores como el tiempo de espera y la línea de comandos del kernel después de que ya se haya leído su archivo. Su valor de GRUB_TIMEOUT=10 en /etc/default/grub es sobrescrito unos instantes después por un archivo del proveedor que lo establece en 0. El comando grep anterior muestra las asignaciones exactas de su imagen, así que consulte esas asignaciones en lugar de confiar en esta explicación.
La regla práctica es la siguiente: coloque sus propios valores en un archivo que aparezca el último en el orden de clasificación, como /etc/default/grub.d/99-local.cfg, en lugar de editar /etc/default/grub. Así, ningún archivo incluido en la imagen podrá aparecer después.
Por qué GRUB_FORCE_PARTUUID hace irrelevante la selección del menú
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 indica al generador que busque el sistema de archivos raíz mediante el UUID de la partición. El generador escribe este valor directamente en la línea de comandos del kernel como root=PARTUUID=..., en lugar de buscar un UUID del sistema de archivos durante el arranque. El proveedor de la imagen lo establece porque permite que una sola imagen de disco arranque de forma fiable en hardware distinto del que se usó para crearla. El segundo grep muestra el código que procesa la variable en /etc/grub.d/10_linux. Ese script está en su propio disco y es la referencia que determina el comportamiento de su imagen.
La consecuencia es lo importante aquí: por esa ruta, el generador escribe una entrada de arranque directa en lugar de una lista completa de los kernels instalados. Compruebe cuántas entradas se han generado.
sudo grep -cE '^\s*(menuentry|submenu) ' /boot/grub/grub.cfg
sudo grep -nE '^\s*(menuentry|submenu) ' /boot/grub/grub.cfgSi el resultado es 1, no existe una segunda entrada que seleccionar. Por tanto, GRUB_DEFAULT=1 hace referencia a una entrada inexistente. GRUB no puede resolverla y arranca la primera entrada, que es el kernel nuevo que intentaba evitar. grub-set-default tampoco ayuda, porque el problema no está en la entrada predeterminada. El menú que intenta seleccionar nunca se generó.
Para recuperar el menú completo, aparte el archivo del proveedor y previsualice el resultado antes de aplicarlo. grub-mkconfig sin -o escribe en la salida estándar y no modifica nada en el disco.
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 el recuento pasa de 1 a varias entradas, significa que las entradas aparecen cuando se elimina el forzado. Todavía no se ha escrito nada. Vuelva a colocar el archivo si el segundo recuento no es correcto, porque PARTUUID forzado es el mecanismo que usa la imagen del proveedor para localizar el sistema de archivos raíz. Al eliminarlo, el equipo pasa a utilizar la ruta de búsqueda en su lugar. Cree una instantánea antes de ejecutar update-grub realmente.
Si su único objetivo es superar el problema de un kernel defectuoso, deténgase aquí y use las opciones más seguras que aparecen más abajo. Reconstruir el menú de arranque de un servidor remoto para evitar una sola actualización implica más riesgo del que justifica el problema.
Por qué los números de entrada no son adecuados para fijar una entrada
GRUB_DEFAULT acepta un número, un título o un identificador. Los números cuentan las entradas de nivel superior desde 0. Una entrada anidada usa > como separador, por lo que GRUB_DEFAULT="1>2" significa la entrada del índice 2 dentro del submenú del índice 1.
Los índices cambian. 10_linux muestra los kernels del más reciente al más antiguo, de modo que instalar un kernel desplaza cada entrada anterior una posición hacia abajo y eliminar un kernel las desplaza hacia arriba. Tu cuidadoso 1>2 sigue siendo válido después de ese cambio. Ahora identifica otro kernel. No se produce ningún error ni aparece ninguna advertencia. Lo descubres después de reiniciar.
Los identificadores no cambian, porque cada uno contiene la versión del kernel. Lee los tuyos:
sudo awk -F"'" '/menuentry_id_option/ {print $2, "==>", $4}' /boot/grub/grub.cfgIgnora las primeras líneas de la salida. Corresponden a la variable que se define en la cabecera. Después, el lado izquierdo es el título que ve el usuario y el lado derecho es el identificador que pasas a las herramientas. Para una entrada dentro de un submenú, une el identificador del submenú y el identificador de la entrada con >, en ese orden, exactamente igual que en la forma numérica.
Arrancar una vez con el kernel anterior mediante grub-reboot
Una selección de un solo arranque es la opción adecuada en un servidor remoto porque se revierte por sí sola. grub-reboot escribe next_entry en /boot/grub/grubenv. GRUB lee esa variable, la borra y guarda el valor borrado antes de arrancar nada. Por tanto, un kernel que provoca un panic no se vuelve a intentar en el siguiente arranque. Sólo hay un intento y, después, la máquina vuelve por sí sola a su valor predeterminado normal.
Primero, confirme que la configuración generada lee esa variable:
sudo grep -n -B2 -A5 'next_entry' /boot/grub/grub.cfgDebe aparecer una línea load_env y un bloque que establezca default a partir de next_entry. Si grep no muestra nada, la imagen nunca lee grubenv durante el arranque. En ese caso, grub-reboot se aceptará en el shell y el gestor de arranque lo ignorará. Es la misma ruta de arranque directo forzado de la sección anterior, pero presente en un segundo lugar.
sudo grub-reboot '<the identifier you copied>'
sudo grub-editenv listgrub-editenv list debería mostrar ahora una línea next_entry= que contenga exactamente lo que pasó. Abra la consola de su proveedor en una pestaña del navegador. Después, reinicie y compruebe el resultado.
sudo rebootuname -rSi uname -r muestra la versión anterior, el bloqueo funcionó. Si muestra la versión nueva, significa que el identificador no se resolvió o que grubenv no se está leyendo. En cualquier caso, la máquina está disponible, que es precisamente el objetivo de usar la forma de un solo arranque.
Haz que la selección persista con GRUB_DEFAULT=saved
GRUB_DEFAULT=saved hace que el valor predeterminado proceda de saved_entry en grubenv, y ese valor se establece con grub-set-default. Se conserva al instalar kernels, porque update-grub vuelve a generar grub.cfg y nunca modifica 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.cfgEl último comando debe mostrar set default="${saved_entry}". Si muestra set default="0", algo cargado después de su archivo volvió a establecer GRUB_DEFAULT como un valor literal. Por tanto, vuelva a enumerar /etc/default/grub.d/ y compruebe que 99-local.cfg realmente queda el último en el orden.
GRUB_SAVEDEFAULT=true es una configuración diferente y es fácil confundirla con esta. Guarda lo que acaba de arrancar como el nuevo valor predeterminado, por lo que este sigue al último arranque correcto. En un servidor, un reinicio desatendido puede cambiar silenciosamente su selección fijada. Déjela desactivada, salvo que eso sea lo que quiere.
Una selección fijada por identificador también puede fallar. Si elimina el kernel que identifica, el identificador deja de resolverse y vuelve a la primera entrada. Por tanto, mantenga también el paquete o excluya ese kernel de autoremove.
Cómo mostrar el menú en la consola del proveedor
La selección interactiva necesita que el menú aparezca en pantalla, pero las imágenes de la nube lo ocultan. Añada estas líneas al archivo que se ordene en último lugar y ejecute sudo update-grub.
GRUB_TIMEOUT=10
GRUB_TIMEOUT_STYLE=menu
GRUB_RECORDFAIL_TIMEOUT=10GRUB_TIMEOUT_STYLE=hidden junto con GRUB_TIMEOUT=0 no muestra nada, por lo que quien observa la consola ve que los mensajes del kernel aparecen de inmediato y concluye que se omitió el cargador de arranque. GRUB_RECORDFAIL_TIMEOUT es el tiempo de espera independiente que se usa después de un arranque que no se completó. Las imágenes de la nube también lo establecen en 0. Por eso, un servidor que acaba de fallar durante el arranque tampoco se detiene ni espera su intervención.
Si el proveedor ofrece una consola serie en lugar de una consola gráfica y sigue sin ver nada, GRUB está escribiendo en un terminal que no puede ver. Añada las dos líneas juntas: la primera selecciona las salidas y la segunda configura el puerto:
GRUB_TERMINAL="console serial"
GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1"A partir de ahora se añadirán diez segundos a cada arranque. Vuelva a establecer el tiempo de espera en 0 cuando haya terminado.
Opciones más seguras que editar el bootloader
Cambiar la entrada del bootloader en una máquina a la que sólo accede por SSH es la opción de mayor riesgo de esta página. Existen alternativas más sencillas y normalmente resuelven el problema real.
Mantenga los paquetes del kernel. Si el objetivo es «no me instale un kernel más nuevo», indíqueselo al gestor de paquetes en lugar de hacerlo en el 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 showholdUse los nombres que haya mostrado el primer comando, porque las imágenes de nube suelen instalar la variante virtual o kvm en lugar de generic. apt upgrade omite los paquetes retenidos y lo indica con The following packages have been kept back:. Las actualizaciones desatendidas en Ubuntu también los omiten. El coste es real: un kernel retenido deja de recibir correcciones de seguridad. Trátelo como una pausa con una fecha definida y libérelo con sudo apt-mark unhold. Si evita las actualizaciones del kernel porque los reinicios provocan una interrupción y no porque un kernel concreto sea defectuoso, el parcheo dinámico del kernel en un VPS resuelve ese problema.
Cree una instantánea antes de actualizar. Una instantánea se restaura en minutos, sin escribir en la consola y sin riesgo de dejar un cambio parcial en el bootloader. Cree la instantánea, actualice, reinicie y compruebe el sistema. Si el kernel nuevo presenta problemas, revierta la instantánea y la ruta de arranque quedará exactamente como estaba.
Use la consola o una imagen de rescate si el equipo ya está detenido. Cuando el servidor ya no arranca, la configuración del bootloader no es el lugar donde debe corregirlo. Esa recuperación sigue su propio procedimiento: qué hacer cuando un VPS no arranca después de una actualización del kernel.
Qué falla y qué mensaje verá
Su edición de /boot/grub/grub.cfg desapareció. Se instaló o eliminó un paquete del kernel, se ejecutó su script del mantenedor update-grub y el archivo se volvió a generar a partir de las entradas. La cabecera # DO NOT EDIT THIS FILE indica las dos ubicaciones de entrada. Edite esas ubicaciones.
grub-editenv: error: environment block too small. Falta /boot/grub/grubenv o está truncado. Vuelva a crearlo con sudo grub-editenv /boot/grub/grubenv create, establezca de nuevo su valor y confírmelo con sudo grub-editenv list.
Un kernel fijado genera un panic con VFS: Unable to mount root fs on unknown-block(0,0). La entrada fijada apunta a un kernel o initrd que ya no está en el disco, normalmente porque se eliminó el paquete mientras el identificador permanecía en grubenv. La recuperación consiste en arrancar desde la consola con una entrada operativa y borrar después el valor obsoleto.
uname -r no cambia después de un reinicio en el que esperaba que cambiara. Compruebe tres cosas, en este orden: ¿grub-editenv list todavía muestra su valor o ya se consumió? ¿El identificador que estableció aparece en el grub.cfg actual? ¿grub.cfg contiene una línea set default que lea la variable que estableció? Una de esas tres comprobaciones lo explica siempre.
El menú apareció por sí solo después de un fallo. GRUB registra un arranque fallido en grubenv como recordfail=1 y eso fuerza la aparición del menú en el siguiente arranque para que una persona pueda intervenir. Bórrelo con sudo grub-editenv /boot/grub/grubenv unset recordfail cuando la máquina vuelva a estar operativa.
La única frase que conviene conservar es esta: el archivo que edita no es el archivo que GRUB lee, y en una imagen de nube la diferencia entre ambos es el origen de la confusión. Lea primero la configuración generada. Todas las decisiones de esta página se basan en lo que realmente indica.
FAQ
¿Por qué GRUB_DEFAULT=1 no cambia el kernel con el que arranca mi VPS?
Porque en una imagen cloud de Ubuntu, el archivo `/boot/grub/grub.cfg generado suele contener una sola entrada de arranque. Por tanto, el índice 1 no identifica ninguna entrada y GRUB vuelve a la primera. Confírmelo con sudo grep -cE '^\s*(menuentry|submenu) ' /boot/grub/grub.cfg. Un recuento de 1 lo confirma. La causa es GRUB_FORCE_PARTUUID, que el proveedor de la imagen establece en un archivo ubicado bajo /etc/default/grub.d/. Esto hace que el generador use una ruta de arranque directa en lugar de crear una lista completa de kernels instalados. Busque el archivo con grep -rn GRUB_FORCE_PARTUUID /etc/default/grub /etc/default/grub.d/`.
¿Cómo arranco el kernel anterior una sola vez?
Ejecute `sudo grub-reboot '<identifier>' con un identificador copiado de su propio grub.cfg y reinicie con la consola del proveedor ya abierta. GRUB borra next_entry antes de arrancar. Por tanto, la selección se aplica a un solo intento y no se vuelve a probar un kernel que entre en pánico. Confirme que el valor se haya establecido con sudo grub-editenv list. Antes de depender de esta opción, ejecute sudo grep -n next_entry /boot/grub/grub.cfg, porque una imagen cuya configuración nunca carga grubenv` ignorará el comando sin mostrar ningún error.
¿Debo fijar la selección por número de entrada o por identificador?
Por identificador. Los números de entrada son posiciones en una lista que `10_linux reconstruye con las versiones más recientes primero. Por ello, instalar o eliminar cualquier kernel cambia esas posiciones. Además, un 1>2 obsoleto todavía puede resolver a una entrada real, pero incorrecta, sin mostrar ningún aviso. Los identificadores contienen la versión del kernel. Por tanto, coinciden con el kernel previsto o no se pueden resolver. Liste los identificadores con sudo grep -n menuentry_id_option /boot/grub/grub.cfg` y copie la cadena entre comillas que aparece después de cada línea de entrada.
¿Es más seguro retener el paquete del kernel que cambiar el gestor de arranque?
Para el objetivo habitual, sí. `sudo apt-mark hold linux-image-virtual linux-headers-virtual impide que llegue un kernel más reciente. Así, la ruta de arranque no cambia y no hay nada que configurar incorrectamente desde una consola a la que quizá no tenga acceso. Compruebe primero los nombres de las variantes instaladas en su propio equipo con apt list --installed y verifique la retención con apt-mark showhold. La desventaja es que un kernel retenido no recibe correcciones de seguridad. Por tanto, decida cuándo ejecutará sudo apt-mark unhold` antes de aplicar la retención.