手元にあった2012年のMac miniへUbuntu 24.04を入れ、開発用の常時稼働サーバとして使うことにしました。

SSHで入り、Dockerを動かし、今後はsystemdに開発用の常駐サービスも任せる予定です。古いMac miniですが、こうした用途ならまだ十分使えます。

ところが構築を進めている途中で、少し分かりにくい問題に遭遇しました。

SSHには普通に接続できる。Dockerも動いている。systemctl --failed を見ても失敗したユニットは0件。それなのにsystemd全体の状態だけが、いつまで待っても running になりません。

systemctl is-system-running
starting

最終的に分かったのは、Mac miniをモニターなしでヘッドレス起動したとき、Plymouthの終了待ちが完了せず、plymouth-quit-wait.service がブート完了を止めていたということでした。

今回はPlymouthのパッケージ削除やsystemdユニットのmaskではなく、GRUBのカーネル引数でPlymouthを無効化することで解消しています。

この記事は、自分が実際に遭遇した問題を調べた記録です。同じMac miniでなくても、Ubuntuをヘッドレス運用していて「サービスは動いているのにsystemdが starting のまま」という症状に遭遇した人が、切り分けの手掛かりとして使えるようにまとめます。

環境

今回使っている環境は次の通りです。

Mac mini: Macmini6,2(2012年モデル)
OS: Ubuntu 24.04.2 LTS
用途: 開発用の常時稼働サーバ
運用: モニターを接続しないヘッドレス運用
GPU: Intel i915
Display Manager: GDM

調査時に確認した主なパッケージは次のバージョンでした。

plymouth 24.004.60-1ubuntu7
gdm3     46.2-1ubuntu1~24.04.1
systemd  255.4-1ubuntu8.17

問題が最初に発生した時点ではsystemdは 255.4-1ubuntu8.5 でした。その後 255.4-1ubuntu8.17 へ更新された状態でも、モニターなしで同じ症状を再現できています。

そのため、少なくとも自分の環境では「systemdの特定バージョンだけが原因だった」とは考えていません。

failedは0なのに起動が終わらない

最初に確認したのは、いつものsystemdの状態です。

systemctl is-system-running
systemctl --failed

結果は次のようになっていました。

systemctl is-system-running -> starting
systemctl --failed          -> 0 failed units

この組み合わせが少し厄介でした。

systemctl --failed が0だと、ぱっと見では正常に見えます。しかもSSHもDockerも利用できていたため、「実際にはもう起動しているのでは」と思いたくなります。

しかし、failed が0なのは「失敗したユニットがない」というだけです。完了せず待ち続けているジョブがある場合は、failedには出てきません。

今後このMac miniでsystemd管理の常駐サービスを動かす予定だったため、ブートトランザクションが終わっていない状態をそのままにするのは避けることにしました。

systemctl list-jobs で待ち続けているものを探す

次に、まだ残っているジョブを確認しました。

systemctl list-jobs

すると、次のようなジョブが残っていました。

multi-user.target
graphical.target
setvtrgb.service
system-getty.slice
systemd-update-utmp-runlevel.service
plymouth-quit-wait.service

その中で特に目立ったのが plymouth-quit-wait.service です。

systemctl status plymouth-quit-wait.service --no-pager

このサービスは起動直後から activating のままで、実際には次のプロセスが待ち続けていました。

/usr/bin/plymouth --wait

plymouthd も生きたままです。

UbuntuのPlymouthコマンドのman pageでも、--waitplymouthd が終了するのを待つ処理として説明されています。

ここでようやく、systemdそのものが壊れているというより、Plymouthの終了を待つ処理が戻ってこないため、その後ろにあるtargetまで完了できない状態だと分かりました。

Plymouthをすぐ無効化しなかった理由

PlymouthはLinuxの起動中にスプラッシュ画面を表示する仕組みです。

今回のMac miniはモニターを接続せず、SSHで使う開発サーバです。起動スプラッシュは必要ありません。

それなら最初からPlymouthを消してしまえばよさそうですが、ここではすぐに変更しませんでした。

Plymouthはsystemdのブート処理やDisplay Managerとの引き継ぎにも関係します。原因を確認しないままパッケージを削除したり、関連ユニットをmaskしたりすると、「systemdを running にするためだけの変更」になってしまいます。

自分が知りたかったのは、表示上の状態を変える方法ではなく、なぜこのMac miniだけ起動完了まで進まないのかでした。

GDMは起動しているのにPlymouthが終わらない

調べてみると、GDM自体は正常に起動していました。

gdm.service -> active (running)

一方で、plymouth-quit-wait.service は終了待ちのままです。

ブートログを見ると、Plymouth関連サービスの開始後にGDMも起動し、GDM自体は Started まで進んでいました。それでもPlymouthが終了せず、plymouth --wait が残っています。

この時点では、次のような流れを疑いました。

GDMは起動する
  -> GDMとPlymouthの引き継ぎが完了しない
  -> plymouthdが終了しない
  -> plymouth --waitが戻らない
  -> plymouth-quit-wait.serviceが完了しない
  -> multi-user.target / graphical.targetも完了しない
  -> systemdがstartingのままになる

ただし、これだけでは「なぜ引き継ぎが終わらないのか」は分かりません。

正常だった起動と比べてみる

原因をかなり絞れたのは、同じMac miniの過去の正常起動ログと比較したときでした。

以前、モニターを接続した状態で起動したときは、systemdは正常に running まで到達していました。

一方、問題が発生していた起動ではモニターを接続していません。

正常だった起動: モニター接続あり
問題の起動:     モニター接続なし

正常起動では、i915のフレームバッファ初期化が確認できました。

fbcon: i915drmfb (fb0) is primary device
i915 ... fb0: i915drmfb frame buffer device

その後、GNOME ShellからGDMへのセッション登録も行われています。

gnome-shell: Registering session with GDM
systemd: Finished plymouth-quit-wait.service
systemd: Reached target multi-user.target
systemd: Reached target graphical.target

ところが、モニターなしの起動ではi915側に次のログが出ていました。

i915 ... Cannot find any crtc or sizes

確認したDRM connectorもすべて disconnected です。

さらに、正常起動で出ていた次のログがありませんでした。

gnome-shell: Registering session with GDM

ここで初めて、モニターを接続しているかどうかが単なる偶然ではなく、Plymouthが終わらないことと関係していそうだと考えました。

モニターなしでもう一度起動して再現を確認した

一度だけなら、たまたまそのブートだけ失敗した可能性があります。

そこでsystemdが更新された後にも、もう一度モニターを外した状態で起動しました。

結果は同じでした。

systemctl is-system-running -> starting
systemctl --failed          -> 0 failed units
plymouth-quit-wait.service  -> activating
multi-user.target           -> waiting
graphical.target            -> waiting
DRM connectors              -> all disconnected
i915                        -> Cannot find any crtc or sizes

GDMとWaylandのgreeter自体は起動しています。しかし、正常起動で確認できた Registering session with GDM はありません。

SSHとDockerは、この止まった状態でも利用できました。

これで、少なくとも自分のMac miniでは、モニターなしのヘッドレス起動が再現条件になっていると判断できるようになりました。

今回の原因としてどこまで言えるか

自分が確認できた範囲では、次の流れが最も妥当です。

Mac miniをヘッドレス起動
  -> i915に接続されたCRTC / displayがない
  -> GDM / Wayland greeterは起動する
  -> 正常時に見られたGDMへのRegisterSession経路まで進まない
  -> GDM / Plymouthの通常の終了引き継ぎが完了しない
  -> plymouth --waitが戻らない
  -> plymouth-quit-wait.serviceがtargetの完了を止める
  -> systemdがstartingのままになる

ただし、ここは断定しすぎないようにしています。

「CRTCがない状態が、GDM内部のどの処理を通じてPlymouth終了を止めているのか」までは確認できていません。

そのため、「Ubuntu 24.04のGDMにはこのバグがある」「i915が原因である」と一般化するつもりはありません。

今回確認できたのは、Macmini6,2のこの構成ではモニターなしで症状を再現でき、実際にブートを止めているのは plymouth-quit-wait.service の終了待ちだった、というところまでです。

開発サーバならPlymouthを起動経路から外してよいと判断した

ここまで調べたうえで、対処方針を決めました。

このMac miniの目的はデスクトップとして使うことではなく、モニターなしで動かす開発サーバです。Plymouthの起動画面は必要ありません。

そこでi915やGDM側を無理に調整するのではなく、サーバ用途では不要なPlymouthをブートのクリティカルパスから外すことにしました。

Plymouthパッケージは削除していません。関連するsystemdユニットもmaskしていません。

変更したのは、GRUBから渡すカーネル引数だけです。

Ubuntuのkernel command lineのman pageでも、plymouth.enable= はPlymouthのboot splashを無効化するためのパラメータとして定義されています。

実際に行った対処

1. 現在のGRUB設定を確認する

まず /etc/default/grub の対象行を確認しました。

grep '^GRUB_CMDLINE_LINUX_DEFAULT=' /etc/default/grub

自分の環境では次の設定でした。

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"

2. 変更前の設定をバックアップする

ブートに関わる設定なので、先にそのまま戻せるコピーを作りました。

sudo cp -a /etc/default/grub /etc/default/grub.before-plymouth-disable

SSH経由で作業する場合は、設定を触る前にSSHが正常に動いていることや、起動に失敗した場合に物理的に復旧できるかも確認しておいた方が安全です。

3. Plymouthを無効化する

設定ファイルを編集します。

sudoedit /etc/default/grub

自分の環境では次の行を、

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"

次のように変更しました。

GRUB_CMDLINE_LINUX_DEFAULT="quiet plymouth.enable=0"

既存の設定が異なる場合は、この行をそのままコピーして上書きしない方がよいです。

今回必要なのは、既存のカーネル引数を壊さず、splash を外して plymouth.enable=0 を追加することです。

4. GRUB設定を再生成する

内容を確認した後にGRUBを更新します。

sudo update-grub

生成された設定へ引数が入ったことも確認しました。

sudo grep -n 'plymouth.enable=0' /boot/grub/grub.cfg | head

5. モニターを接続せずに再起動する

今回は「ヘッドレス起動で直ったか」を確認したかったため、モニターは接続せず、そのまま再起動しました。

sudo reboot

リモートマシンのブート設定を変更して再起動する操作なので、SSHが戻らなかったときの復旧手段がない環境では慎重に行う必要があります。

再起動後、systemdはrunningまで到達した

再起動後、まずカーネル引数を確認しました。

cat /proc/cmdline

plymouth.enable=0 が反映されています。

続いて、問題になっていたsystemd全体の状態を確認します。

systemctl is-system-running
systemctl --failed
systemctl list-jobs

結果は次のようになりました。

systemctl is-system-running -> running
systemctl --failed          -> 0 failed units
systemctl list-jobs         -> No jobs running

関連するサービスとtargetも確認しました。

systemctl status plymouth-start.service --no-pager
systemctl status plymouth-quit-wait.service --no-pager
systemctl is-active multi-user.target
systemctl is-active graphical.target
systemctl is-active ssh.socket
systemctl is-active ssh.service
systemctl is-active docker.service

自分の環境では次の状態になっています。

plymouth-start.service      -> inactive、条件不成立でskip
plymouth-quit-wait.service  -> active (exited)、status=0/SUCCESS
multi-user.target           -> active
graphical.target            -> active
ssh.socket                  -> active
ssh.service                 -> active
docker.service              -> active

これで、モニターなしでもブートトランザクションが正常に完了するようになりました。

i915のログは残ったまま。それで問題ない

対処後も、ヘッドレス起動時の次のログは残っています。

i915 ... Cannot find any crtc or sizes

ここは今回の対処を理解するうえで重要でした。

GRUBでPlymouthを無効化しても、「モニターが接続されていない」というグラフィックス側の状態そのものは変わりません。

つまり今回の対処は、i915の状態を直したわけでも、GDMに仮想的なディスプレイを認識させたわけでもありません。

ヘッドレス時のグラフィックス側の状態はそのままに、開発サーバでは不要なPlymouthの終了待ちをブート完了の条件から外したということです。

自分の用途では、この方が余計な変更が少なく、目的にも合っていました。

元に戻す方法

問題が出た場合や、再びPlymouthを有効にしたい場合は、保存した設定へ戻せます。

sudo cp -a /etc/default/grub.before-plymouth-disable /etc/default/grub
sudo update-grub

その後、設定内容を確認したうえで再起動します。

sudo reboot

自分の環境で変更前の設定へ手動で戻す場合は、次の状態です。

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"

同じ症状が出たときに最初に見るところ

同じように systemctl is-system-runningstarting のままでも、原因がPlymouthとは限りません。

まずは次の順番で確認するのがよいと思います。

systemctl is-system-running
systemctl --failed
systemctl list-jobs
systemctl status plymouth-quit-wait.service --no-pager
pgrep -a plymouth
journalctl -b --no-pager | grep -Ei 'plymouth|gdm|i915|drm'

今回と同じ症状なら、特に次の組み合わせが手掛かりになります。

systemctl is-system-running -> starting
systemctl --failed          -> 0
plymouth-quit-wait.service  -> activating
/usr/bin/plymouth --wait    -> 残っている
plymouthd                    -> 残っている
multi-user.target            -> waiting

さらに、そのマシンがモニターありでは正常に起動し、モニターなしで再現するなら、今回とかなり近い状況です。

逆に、failed unitがある、ストレージやネットワークのmount待ちが残っている、別のserviceが activating のままになっている、といった場合は、Plymouthを無効化する前にそちらを調べるべきです。

あとがき:古いPCをサーバにすると、デスクトップ向けの前提が残ることがある

今回おもしろかったのは、SSHもDockerも正常に動いているのに、ブート全体としては完了していなかったことです。

開発サーバとして触っていると、SSHでログインできた時点で「起動した」と考えがちです。自分も最初はそう見えました。

ただ、systemdの状態を追っていくと、実際にはPlymouthが終わるのをずっと待っていました。そして正常起動と比較すると、違っていたのはモニターの有無でした。

Mac mini 2012はもともとデスクトップ用途のマシンです。そこへUbuntu Desktopを入れ、さらにモニターを外してサーバとして使うと、GDMやPlymouthのような「画面があることを前提にした起動経路」が思わぬところで残ることがあります。

今回の経験から一番残しておきたいのは、systemctl --failed が0でも安心しきらず、systemctl list-jobs まで見ることです。

また、原因らしいものを見つけても、いきなりサービスをmaskしたり削除したりするより、正常だった起動との差を比較した方が原因を絞りやすいと感じました。今回はモニターあり/なしという単純な差が、かなり重要な手掛かりになりました。

もし同じように古いMacやPCをUbuntuのヘッドレスサーバへ転用していて、SSHには入れる、Dockerも動く、failed unitもない、それなのにsystemdだけ starting のままという状態になったら、plymouth-quit-wait.service とモニター接続の有無を一度確認してみてください。

少なくとも自分のMacmini6,2では、それが問題を解く入口になりました。