SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-26

Ubuntu VPSで次回起動するkernelを固定する方法

Ubuntuのcloud imageでGRUB_DEFAULTが効かない原因を確認します。実際のmenu entryを読み取り、SSH接続を失わず次回起動するkernelを安全に固定する手順です。

VPS が次回起動するカーネルを決めるもの

VPS が次回起動するカーネルは、1 つの生成ファイルである /boot/grub/grub.cfg によって決まります。このファイルを直接編集することはありません。入力ファイルを編集してから、ファイルを再生成します。Ubuntu のクラウドイメージでは、入力ファイルの 1 つがイメージ提供元から追加されます。その内容によってメニュー選択が無効になる場合があります。そのため、レンタルサーバーでは GRUB_DEFAULT=1 の後に update-grub を実行しても何も変わらない一方、同じ 2 つの手順がノート PC のインストール環境では機能します。

次の順序で作業します。まず、カーネルを自分で選択できるか確認します。提供元が追加したファイルを含め、すべての入力ファイルを読みます。生成された出力を読み、実際に含まれるエントリ数を数えます。その後で、固定方法を選びます。SSH でしか接続できないマシンでこの順序を誤ると、レスキューコンソールが必要になります。そのため、最も安全な方法はこのページの最後に示すものであり、多くの場合、それが適切な選択です。

まず、固定対象が自分の kernel か確認します

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

systemd-detect-virt に kvm、qemu、または xen と表示される場合は、自分で kernel を実行しているため、以下の内容がすべて適用されます。lxc または openvz と表示される場合、サーバーはホストの kernel を共有しています。そのため、自分の bootloader は存在せず、固定できるものもありません。この場合、uname -r が報告するバージョンは /boot/vmlinuz-* にはまったく表示されません。実行中の kernel はホストのものであり、ディスク上の設定では変更できないためです。

ls -1 /boot/vmlinuz-* は、実際に選択可能な kernel の一覧です。1 行しかない場合、以前の kernel はすでに削除されており、bootloader の設定で復元することはできません。通常は autoremove 中に発生します。管理対象のサーバーで Ubuntu で古い kernel を削除する前に、その仕組みを理解しておくとよいでしょう。

編集するファイルは、GRUB が読み取るファイルではありません

/etc/default/grub には、シェル変数の代入がそのまま記述されています。これは入力です。/boot/grub/grub.cfg が出力で、# DO NOT EDIT THIS FILE とその理由で始まります。出力に直接書き込んだ内容は、次にカーネルパッケージをインストールまたは削除した時点で失われます。パッケージスクリプトが出力を再生成するためです。

cat /usr/sbin/update-grub

update-grub はラッパーです。grub-mkconfig -o /boot/grub/grub.cfg を実行すると、変数を読み込み、/etc/grub.d/ 内のすべてのスクリプトを実行し、結果を書き出します。2 つのコマンドで、方向は 1 つです。入力を渡すと、grub.cfg が生成されます。

設定を上書きする場所: /etc/default/grub.d

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

2 つ目のパスは見落とされがちな部分です。grub-mkconfig は最初に /etc/default/grub を読み込み、その後 /etc/default/grub.d/ 内のすべての *.cfg ファイルを glob の順序で読み込みます。実際の処理を確認してください。

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

読み込みは通常の shell 処理なので、最後に代入された値が有効になります。Ubuntu cloud image はこのディレクトリにファイルを配置し、timeout や kernel command line などの値を、あなたのファイルが読み込まれた後に設定します。/etc/default/grub 内の GRUB_TIMEOUT=10 は、直後に値を 0 に設定する vendor ファイルによって上書きされます。上の grep は、使用している image 上の実際の代入内容を出力します。そのため、この説明をそのまま信頼せず、出力された内容を確認してください。

ここから導かれる実用的なルールは、/etc/default/grub を編集するのではなく、/etc/default/grub.d/99-local.cfg のように名前順で最後に処理されるファイルへ独自の設定を記述することです。そうすれば、image に含まれるファイルが後から設定を上書きすることはありません。

GRUB_FORCE_PARTUUID によってメニューの選択が無意味になる理由

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 は、起動時にファイルシステム UUID を検索するのではなく、パーティション UUID で root ファイルシステムを検索するようジェネレーターに指示します。この値は root=PARTUUID=... としてカーネルコマンドラインに直接書き込まれます。イメージベンダーがこの設定を行うのは、構築元とは異なるハードウェアでも 1 つのディスクイメージを確実に起動できるようにするためです。2 つ目の grep では、/etc/grub.d/10_linux 内でこの変数を処理するコードを確認できます。このスクリプトは自分のディスク上にあり、使用しているイメージの動作を決める実体です。

ここで重要なのは、その経路ではジェネレーターがインストール済みカーネルの完全な一覧ではなく、直接起動するエントリを書き込むことです。最終的にいくつ生成されたかを数えてください。

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

数が 1 なら、選択できる 2 つ目のエントリはありません。そのため、GRUB_DEFAULT=1 は存在しないエントリを指定しています。GRUB はそれを解決できず、最初のエントリを起動します。これは、避けようとしていた新しいカーネルです。grub-set-default でも解決しません。問題はデフォルト設定ではないためです。選択しようとしているメニュー自体が生成されていません。

完全なメニューを戻すには、ベンダーのファイルを別の場所へ移動し、適用する前に結果をプレビューしてください。grub-mkconfig を -o なしで実行すると、標準出力に書き出すだけで、ディスク上の内容は変更しません。

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) '

数が 1 から複数へ増えた場合、強制指定を解除するとエントリが表示されるということです。まだ何も書き込まれていません。2 回目の結果が適切でなければ、ファイルを元に戻してください。強制された PARTUUID は、プロバイダーのイメージが root ファイルシステムを見つけるために使用しているためです。これを削除すると、マシンは検索経路を使用する構成に移行します。update-grub を実際に実行する前に、スナップショットを取得してください。

目的が 1 つの問題のあるカーネルを回避することだけなら、ここで止めて、後述するより安全な方法を使用してください。1 回のアップグレードから復旧するためにリモートサーバーのブートメニューを再構築するのは、問題に対してリスクが大きすぎます。

エントリー番号を固定すべきでない理由

GRUB_DEFAULTは、番号、タイトル、識別子を受け取ります。番号はトップレベルのエントリーを0から数えます。ネストされたエントリーでは区切り文字として>を使うため、GRUB_DEFAULT="1>2"は、インデックス1のサブメニュー内にあるインデックス2のエントリーを意味します。

インデックスは変動します。10_linuxはカーネルを新しい順に並べるため、カーネルをインストールすると古いエントリーがすべて1つずつ後ろへ移動し、カーネルを削除すると前へ移動します。その後も、設定した1>2はエラーなく解釈されます。ただし、別のカーネルを指すようになります。エラーも警告も表示されないため、再起動して初めて気付くことになります。

識別子は、それぞれにカーネルのバージョンが含まれているため変動しません。識別子を確認します。

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

出力の最初の数行は無視してください。これはヘッダーで定義されている変数です。その後の行では、左側に利用者が見るタイトル、右側にツールへ渡す識別子が表示されます。サブメニュー内のエントリーの場合は、数値形式と同じ順序で、サブメニューの識別子とエントリーの識別子を>で連結します。

grub-reboot で前回の kernel を1回だけ起動する

リモートサーバーでは、1回だけ起動先を選ぶ方法が適しています。設定が自動的に元に戻るためです。grub-reboot は /boot/grub/grubenv に next_entry を書き込みます。GRUB はこの変数を読み取り、何かを起動する前に値を消去して、消去後の値を保存します。そのため、kernel が panic しても、次回の起動で同じ kernel が再試行されることはありません。起動は1回だけ試行され、その後はマシンが自動的に通常のデフォルト設定へ戻ります。

まず、生成済みの設定がその変数を実際に読み取るか確認します。

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

load_env の行と、next_entry から default を設定するブロックが必要です。grep の出力がない場合、起動時にイメージが grubenv を読み取っていません。そのため、grub-reboot は shell では受け付けられても、bootloader では無視されます。これは、前のセクションで説明した強制的な直接起動の経路が、別の場所にも現れている状態です。

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

grub-editenv list の出力に、指定した内容と完全に一致する next_entry= の行が表示されるはずです。provider のコンソールをブラウザーのタブで開いてから reboot し、結果を確認します。

sudo reboot
uname -r

uname -r に古い version が表示されれば、指定が機能しています。新しい version が表示される場合は、識別子を解決できなかったか、grubenv が読み取られていません。いずれの場合もマシンは起動しており、これが one shot 形式を使う目的です。

GRUB_DEFAULT=saved で選択を固定する

GRUB_DEFAULT=saved により、デフォルトのエントリは grubenv 内の saved_entry から決まります。この値は grub-set-default で設定します。update-grub は grub.cfg を書き換えますが、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

最後のコマンドは set default="${saved_entry}" を出力する必要があります。set default="0" と出力された場合は、ファイルの後で読み込まれた何かが GRUB_DEFAULT をリテラル値に戻しています。そのため、もう一度 /etc/default/grub.d/ を一覧表示し、99-local.cfg が本当に最後に並ぶことを確認してください。

GRUB_SAVEDEFAULT=true は別の設定であり、この設定と混同しやすいものです。直前に起動したエントリを新しいデフォルトとして保存するため、デフォルトが最後に正常起動したエントリに追従します。サーバーでは、無人再起動によって固定していたエントリが意図せず変わる可能性があります。その動作を意図している場合を除き、無効のままにしてください。

識別子による固定にも、1 つ問題があります。その識別子が示すカーネルを削除すると識別子を解決できなくなり、先頭のエントリに戻ります。そのため、パッケージも hold するか、そのカーネルを autoremove の対象外にしてください。

プロバイダーのコンソールにメニューを表示する

対話的に選択するには、画面にメニューを表示する必要があります。クラウドイメージではメニューが非表示になっているため、これらの設定を最後に読み込まれるファイルに記述し、sudo update-grubを実行します。

GRUB_TIMEOUT=10
GRUB_TIMEOUT_STYLE=menu
GRUB_RECORDFAIL_TIMEOUT=10

GRUB_TIMEOUT_STYLE=hiddenとGRUB_TIMEOUT=0を組み合わせると何も表示されません。そのため、コンソールを監視している人にはカーネルメッセージがすぐに表示され、ブートローダーが省略されたように見えます。GRUB_RECORDFAIL_TIMEOUTは、ブートが完了しなかった場合に使用される別のタイムアウトです。クラウドイメージではこれも0に設定されているため、起動に失敗した直後のサーバーでも停止して待機しません。

プロバイダーがグラフィカルコンソールではなくシリアルコンソールを提供していて、それでも何も表示されない場合、GRUBは見えない端末に出力しています。次の2行を両方追加してください。1行目で出力先を選択し、2行目でポートを設定します。

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

今後のすべての起動に10秒が追加されます。作業が終わったら、タイムアウトを0に戻してください。

ブートローダーを編集するより安全な選択肢

SSH 経由でしか接続できないマシンのブートローダー入力を変更するのは、このページで最もリスクの高い選択肢です。より簡単な方法があり、通常は本当の問題も解決できます。

kernel パッケージを hold する。 目的が「新しい kernel を適用しない」ことであれば、ブートローダーではなくパッケージマネージャーにその意図を指定します。

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

最初のコマンドで表示された名前を使用してください。cloud image では、generic ではなく virtual または kvm の flavour がインストールされることが一般的です。新しい kernel が image の更新時期に現れ、release 自体が変わったのではないかと考えている場合でも、実際にはそうではありません。point release とは、すでに利用している updates を新しいインストールメディアにまとめたものであり、すでに patch が適用されたサーバーに、数週間前から提供されていなかったものを新たに適用するわけではないためです。hold されたパッケージは apt upgrade によって適用対象から外され、The following packages have been kept back: でそのことが通知されます。Ubuntu の unattended upgrades でも同様に適用対象から外されます。代償はあります。hold した kernel には security fix が適用されなくなるため、期限を決めた一時停止として扱い、sudo apt-mark unhold で hold を解除してください。kernel の更新を避ける理由が、特定の kernel に問題があるからではなく、再起動による downtime を避けたいからであれば、VPS での live kernel patching がその問題に対応します。

upgrade 前に snapshot を取得する。 snapshot なら、コンソールで入力する必要がなく、適用途中のブートローダー変更が発生する可能性もなく、数分で復元できます。snapshot を取得し、upgrade して、再起動後に確認します。新しい kernel に問題があれば、ロールバックすることで、ブート経路を元の状態に戻せます。

すでに停止しているサーバーには、コンソールまたは rescue image を使用する。 サーバーが boot しなくなった場合、修正すべき場所はブートローダー設定ではありません。その復旧手順は別途必要です。kernel の更新後に VPS が boot しない場合の対処方法。

何が壊れ、どのメッセージが表示されるか

/boot/grub/grub.cfgへの編集が消えた。 カーネルパッケージがインストールまたは削除され、メンテナースクリプトが update-grub を実行すると、入力元からファイルが再生成されます。# DO NOT EDIT THIS FILE ヘッダーに、2 つの入力場所が示されています。そこを編集してください。

grub-editenv: error: environment block too small。 /boot/grub/grubenv が存在しないか、内容が途中で切れています。sudo grub-editenv /boot/grub/grubenv create で再作成し、値をもう一度設定して、sudo grub-editenv list で確認してください。

固定したカーネルが VFS: Unable to mount root fs on unknown-block(0,0) でパニックになる。 固定したエントリが、すでにディスク上に存在しないカーネルまたは initrd を指しています。通常は、識別子が grubenv に残ったまま、パッケージが削除されたことが原因です。復旧するには、コンソールから動作するエントリで起動し、その後で古い値を消去します。

変更されるはずだった再起動の後も uname -r が変わらない。 次の 3 点を順番に確認してください。grub-editenv list に値がまだ表示されているか、それとも消費されたか。設定した識別子が現在の grub.cfg に表示されているか。grub.cfg に、設定した変数を読み取る set default 行が含まれているか。この 3 点のいずれかで、毎回原因を特定できます。

クラッシュ後、メニューが自動的に表示された。 GRUB は grubenv に起動失敗を recordfail=1 として記録します。これにより、次回の起動時にメニューを表示し、人が介入できるようにします。マシンが正常な状態に戻ったら、sudo grub-editenv /boot/grub/grubenv unset recordfail でこの状態を消去してください。


覚えておくべき文は 1 つです。編集するファイルと GRUB が読み取るファイルは同じではありません。cloud image では、その間の差が混乱の原因になります。まず生成済みの設定を読んでください。このページの判断はすべて、実際にそこへ何が書かれているかに基づきます。

FAQ

GRUB_DEFAULT=1 を設定しても VPS の起動カーネルが変わらないのはなぜですか?

Ubuntu の cloud image では、生成された /boot/grub/grub.cfg に通常 1 つの boot entry しか含まれないためです。そのため、インデックス 1 は存在せず、GRUB は先頭の entry にフォールバックします。sudo grep -cE '^\s*(menuentry|submenu) ' /boot/grub/grub.cfg で確認できます。件数が 1 であれば、これが原因です。原因は GRUB_FORCE_PARTUUID です。これは image vendor が /etc/default/grub.d/ 配下のファイルで設定しており、インストール済みカーネルの完全な一覧を構築せず、generator を直接 boot の経路で動作させます。grep -rn GRUB_FORCE_PARTUUID /etc/default/grub /etc/default/grub.d/ でファイルを探してください。

以前のカーネルを 1 回だけ起動するにはどうすればよいですか?

自分の grub.cfg から identifier をコピーし、sudo grub-reboot '<identifier>' を実行します。その後、provider console を開いた状態で再起動してください。GRUB は起動前に next_entry を消去するため、この選択は 1 回の試行にだけ適用され、panic するカーネルが再試行されることもありません。sudo grub-editenv list で値が反映されたことを確認してください。実行前に sudo grep -n next_entry /boot/grub/grub.cfg を実行してください。設定で grubenv が読み込まれない image では、コマンドがエラーなしで無視されるためです。

entry number と identifier のどちらで固定すべきですか?

identifier を使用してください。entry number は 10_linux が新しい順に再構築する一覧上の位置です。そのため、カーネルをインストールまたは削除すると番号が変わります。古い 1>2 でも実在する別の entry に解決され、警告は表示されません。identifier にはカーネルのバージョンが含まれるため、意図したカーネルに一致するか、解決に失敗します。sudo grep -n menuentry_id_option /boot/grub/grub.cfg で一覧を表示し、各 entry 行の後に続く引用符付き文字列をコピーしてください。

bootloader を変更するより、カーネルパッケージを hold するほうが安全ですか?

通常の目的であれば、はい。sudo apt-mark hold linux-image-virtual linux-headers-virtual を実行すると新しいカーネルが追加されないため、boot path は変わらず、接続できない可能性がある console で操作を誤る余地もありません。まず apt list --installed で自分の環境にインストールされている flavour 名を確認し、apt-mark showhold で hold を確認してください。ただし、hold したカーネルには security fix が適用されません。hold を実行する前に、いつ sudo apt-mark unhold を実行するかを決めてください。