systemd --userのサービスがログアウトで止まる原因とlingering有効化手順

systemd --userのサービスがログアウトで止まる原因とlingering有効化手順

※本記事にはプロモーション(広告・アフィリエイトリンク)を含みます。

Linux機でNodeアプリをsystemdに常駐させ、systemctl --user enable --now まで実行したのに、SSHからログアウトするとサービスが止まってしまう——このページはその一点を解決します。原因はユーザー用systemdマネージャの寿命で、直す鍵は「lingering(居残り)」の有効化です。手順と確認方法、cronから叩くときの落とし穴まで通しで書きます。

なぜログアウトでユーザーサービスが止まるのか

systemctl --user で動くサービスは、ログインごとに起動する ユーザー専用の systemd マネージャ(systemd --user インスタンス) の配下にあります。このマネージャは、そのユーザーのログインセッションに紐づいて生まれます。

systemd の公式ドキュメント(loginctl / logind の説明)によると、通常は ユーザーの最後のセッションが終了すると、そのユーザーの systemd インスタンスも停止 します。SSH でログインして起動したサービスが、ログアウト(=最後のセッション終了)と同時に道連れで落ちるのはこのためです。enable は「次回マネージャ起動時に自動起動する」という予約であって、マネージャ自体を生かし続ける設定ではありません。

これを変えるのが lingering です。lingering を有効にしたユーザーは、ログインしていなくても、マシンの起動直後からユーザーマネージャが立ち上がり、ログアウト後も動き続けます。

状態 ログイン中 ログアウト後 OS再起動後
lingering 無効(既定) 動く 止まる 止まったまま
lingering 有効 動く 動き続ける 自動で起動

ユーザーサービスを用意する

まずユニットファイルを ~/.config/systemd/user/ に置きます。system 側の /etc/systemd/system/ ではない点に注意してください。

# ~/.config/systemd/user/myapp.service
[Unit]
Description=My Node app
After=network-online.target

[Service]
WorkingDirectory=/home/kxkxk/app
ExecStart=/usr/bin/node /home/kxkxk/app/server.js
Restart=always
RestartSec=3

[Install]
WantedBy=default.target

ユーザーサービスの [Install] は WantedBy=multi-user.target ではなく default.target を使います。書いたら読み込んで有効化します。

systemctl --user daemon-reload
systemctl --user enable --now myapp.service
systemctl --user status myapp.service

ここまでで「ログイン中は動く」状態になります。この段階で一度ログアウト→再ログインして status を見ると、止まっていることが確認できるはずです。

lingering を有効化する

本題です。対象ユーザーに対して lingering を有効にします。

# 自分自身に対して
loginctl enable-linger $USER

# 別ユーザーを指定する場合(root 権限が必要)
sudo loginctl enable-linger kxkxk

このコマンドは /var/lib/systemd/linger/<ユーザー名> という空ファイルを作るだけで、ほかの設定ファイルを書き換えません。元に戻すときは次のコマンドで、このファイルが消えます。

loginctl disable-linger $USER

有効化した直後に、ログイン中でなくてもユーザーマネージャが起動するようになります。既に enable 済みのサービスは、マネージャの起動とともに自動で立ち上がります。

効いているかを確認する

設定が入ったかは2通りで確認できます。

# プロパティで確認(yes なら有効)
loginctl show-user $USER --property=Linger

# 実体ファイルで確認
ls -l /var/lib/systemd/linger/

Linger=yes と出ていればOKです。最終確認として、実際にログアウトして別経路(または時間を置いて再SSH)でプロセスが生きているかを見ます。

systemctl --user is-active myapp.service

ログアウト後も active を保っていれば成功です。確実を期すなら OS を再起動し、ログインする前の状態でサービスが上がっているかまで確認してください(起動直後にログインせず、別端末から systemctl --user を叩く方法は次節のとおりです)。

cron やスクリプトから操作するときの落とし穴

ログインシェル以外、たとえば cron や別ユーザーからの実行で systemctl --user ... を叩くと、Failed to connect to bus といったエラーになることがあります。これはユーザーマネージャへの接続に必要な環境変数が、ログインセッションの外では設定されないためです。

XDG_RUNTIME_DIR を明示してから叩くと通ります。

export XDG_RUNTIME_DIR="/run/user/$(id -u)"
systemctl --user restart myapp.service

なお /run/user/<UID> は、lingering が有効なユーザーならログインしていなくても存在します(lingering が無効だと最後のログアウト時に消えます)。この点でも、常駐前提のユーザーには lingering を入れておくほうが扱いが素直になります。環境によってパスや変数名が異なる場合があるので、id -u で自分のUIDを確かめてから設定してください。

公開まで含めて組むなら、サービスをユーザー常駐させたうえで前段にトンネルを置く構成が扱いやすく、Cloudflare Tunnelのingressで複数サブドメインを振り分ける設定例 と組み合わせると、ポート開放なしで複数アプリを出し分けられます。

まとめ

systemctl --user のサービスがログアウトで止まるのは、ユーザーマネージャがセッションに紐づいて消えるためで、loginctl enable-linger $USER で居残りを有効にすれば解決します。次の一手として、loginctl enable-linger を実行したあと 一度ログアウトしてから systemctl --user is-active を確認 し、ログアウト後も動き続けることを自分の目で確かめてください。

よくある質問

systemctl --user enable したのにログアウトで止まるのはなぜ?

enable は次回ユーザーマネージャ起動時の自動起動予約にすぎず、マネージャ自体はログインセッションに紐づいて消えるためです。loginctl enable-linger で居残りを有効にすると解決します。

lingering が有効になっているか確認する方法は?

`loginctl show-user $USER --property=Linger` で Linger=yes なら有効です。実体は /var/lib/systemd/linger/ 配下に空ファイルとして作られるので、ls でも確認できます。

cron から systemctl --user を叩くと bus 接続エラーになる

ログインセッション外では接続用の環境変数が無いためです。export XDG_RUNTIME_DIR="/run/user/$(id -u)" を先に設定してから実行すると通ります。