macOS では、バックグラウンドで動作するサービス(デーモンやエージェント)を管理するために launchd という仕組みが用いられており、これを制御するコマンドが launchctl である。従来は load と unload というサブコマンドが使われていたが、macOS 10.11 El Capitan 以降、これらは「レガシーサブコマンド」として扱われ、代わりに bootstrap と bootout が推奨されるようになった。ただし実際には、2025年現在も多くの記事やスクリプトで従来の load/unload が使われ続けているのが現状である。
本記事では、公式に推奨されている bootstrap と bootout の使い方について解説する。なお、この記事で扱う内容は macOS 10.11 以降を前提としている。
bootstrap と bootout の基本
bootstrap と bootout は、それぞれ従来の load と unload に相当するコマンドである。最大の違いは、ドメインを明示的に指定する必要があるという点だ。
bootstrap: サービスをドメインに登録して起動する(loadの代替)bootout: サービスを停止してドメインから削除する(unloadの代替)
従来の方式では、plist ファイルのパスから暗黙的にドメインが判別されていたが、新しい方式では明示的に指定することで、より正確な制御が可能になるという設計思想である。
ドメインの種類と意味
launchd におけるドメインとは、サービスがどのレベル・どのユーザーのために動作するかを表す概念である。主なドメインは以下の3種類だ。
system ドメイン
システム全体で動作するデーモンのためのドメインである。root 権限で実行され、すべてのユーザーに影響する。通常、/Library/LaunchDaemons/ に配置された plist ファイルがこのドメインで動作する。
gui/UID ドメイン
特定のユーザーがグラフィカルログイン(GUI セッション)している間だけ動作するエージェントのためのドメインである。UID はユーザー ID を表す数値で、macOS では最初に作成されたユーザーは通常 501 となる。自分の UID は id -u コマンドで確認できる。~/Library/LaunchAgents/ や /Library/LaunchAgents/ に配置されたエージェントは、基本的にこのドメインで動作する。
user/UID ドメイン
特定のユーザー権限で動作するが、ログインの有無に関わらず常駐するバックグラウンドサービスのためのドメインである。GUI は使用できないが、PC 起動中は常に動き続ける。このドメインは macOS 10.11 以降で追加された比較的新しい概念である。
基本的な使い方
システムデーモンの登録と削除
システム全体で動作するデーモンを扱う場合、system ドメインを指定する。root 権限が必要なため、sudo を付けて実行する必要がある。
# 登録(起動)
sudo launchctl bootstrap system /Library/LaunchDaemons/com.example.daemon.plist
# 削除(停止)
sudo launchctl bootout system /Library/LaunchDaemons/com.example.daemon.plist
ユーザーエージェントの登録と削除
ログイン中のユーザーのために動作するエージェントを扱う場合、gui/UID ドメインを指定する。まず自分の UID を確認してから、その値を使ってコマンドを実行する。
# UID を確認
id -u
# 例: 501 と表示される
# 登録(起動)
launchctl bootstrap gui/501 ~/Library/LaunchAgents/com.example.agent.plist
# 削除(停止)
launchctl bootout gui/501 ~/Library/LaunchAgents/com.example.agent.plist
スクリプト内で動的に UID を取得する場合は、以下のように記述できる。
#!/bin/bash
# 現在ログイン中のユーザーの UID を取得
USER_ID=$(id -u)
# 登録
launchctl bootstrap gui/$USER_ID ~/Library/LaunchAgents/com.example.agent.plist
# 削除
launchctl bootout gui/$USER_ID ~/Library/LaunchAgents/com.example.agent.plist
サービスの状態確認
登録されているサービスの一覧を確認するには、launchctl list コマンドを使う。特定のサービスを grep で絞り込むことも可能だ。
# すべてのサービスを表示
launchctl list
# 特定のサービスを検索
launchctl list | grep com.example
より詳細な情報を得るには、launchctl print コマンドを使う。
# システムドメインの情報を表示
sudo launchctl print system
# 特定のユーザードメインの情報を表示
launchctl print gui/501
# 特定のサービスの詳細を表示
launchctl print gui/501/com.example.agent
ただし、print コマンドの出力形式は公式には構造化されておらず、バージョン間で変更される可能性があるため、スクリプトでのパース処理には依存しないことが推奨されている。
実行中のサービスの再起動
既に動作しているサービスを再起動したい場合、bootout してから bootstrap し直す方法もあるが、より簡単な方法として kickstart コマンドが用意されている。
# サービスを再起動(-k オプションで既存のインスタンスを終了してから起動)
launchctl kickstart -k gui/501/com.example.agent
-k オプションを付けると、既に動作中のインスタンスを終了してから新しいインスタンスを起動する。オプションなしで実行すると、既に動作中の場合はエラーとなる。
enable と disable の使い分け
bootstrap/bootout とは別に、enable と disable というサブコマンドも存在する。これらはサービスを有効化・無効化するためのもので、次回のブート時やログイン時にサービスを自動起動するかどうかを制御する。
# サービスを無効化(次回から自動起動しない)
sudo launchctl disable system/com.example.daemon
# サービスを有効化(次回から自動起動する)
sudo launchctl enable system/com.example.daemon
注意点として、disable でサービスを無効化した後は、単に bootstrap しようとしてもエラーになる。再度有効にするには、まず enable を実行してから bootstrap する必要がある。
従来の load/unload との対応関係
従来の方式と新しい方式の対応関係をまとめると、以下のようになる。
| 従来の方式 | 新しい方式 |
|---|---|
launchctl load /Library/LaunchDaemons/service.plist | sudo launchctl bootstrap system /Library/LaunchDaemons/service.plist |
launchctl unload /Library/LaunchDaemons/service.plist | sudo launchctl bootout system /Library/LaunchDaemons/service.plist |
launchctl load ~/Library/LaunchAgents/agent.plist | launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/agent.plist |
launchctl unload ~/Library/LaunchAgents/agent.plist | launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/agent.plist |
従来の方式では plist ファイルのパスから自動的にドメインが判別されていたが、新しい方式ではドメインを明示的に指定する必要がある。この変更により、より正確な制御が可能になったとされているが、実際には記述量が増え、コマンドも覚えにくくなったという側面もある。
よくある問題とトラブルシューティング
Permission denied エラー
システムドメインのサービスを操作する際に sudo を付け忘れると、権限エラーが発生する。
# エラー例
launchctl bootstrap system /Library/LaunchDaemons/service.plist
# → Permission denied
# 正しい実行方法
sudo launchctl bootstrap system /Library/LaunchDaemons/service.plist
Operation now in progress エラー
サービスが既に登録されている状態で再度 bootstrap を実行すると、このエラーが発生する。この場合は、まず bootout で削除してから再度 bootstrap する必要がある。
# 一度削除してから再登録
sudo launchctl bootout system /Library/LaunchDaemons/service.plist
sudo launchctl bootstrap system /Library/LaunchDaemons/service.plist
あるいは、前述の kickstart -k コマンドを使うことで、より簡潔に再起動できる。
Bootstrap failed: 5: Input/output error
サービスを disable した後に bootstrap しようとすると、このエラーが発生することがある。この場合は、まず enable で有効化してから bootstrap する必要がある。
# 正しい手順
sudo launchctl enable system/com.example.daemon
sudo launchctl bootstrap system /Library/LaunchDaemons/com.example.daemon.plist
SIP によるエラー
System Integrity Protection(SIP)が有効な場合、システムが管理する一部の plist ファイルは変更できない。このような場合、以下のようなエラーメッセージが表示される。
Operation not permitted while System Integrity Protection is engaged
この場合、基本的には操作を諦めるか、SIP を無効化する必要がある(ただし SIP の無効化はセキュリティリスクを伴うため推奨されない)。
まとめ
bootstrap と bootout は、macOS 10.11 以降で推奨されている launchd サービスの管理コマンドである。従来の load/unload と比べて、ドメインを明示的に指定する必要があるため、より正確な制御が可能になった反面、コマンドが冗長になり覚えにくくなったという側面もある。
実際には、2025年現在も多くの記事やスクリプトで従来の load/unload が使われ続けており、これらのコマンドも依然として動作する。ただし、公式には bootstrap/bootout の使用が推奨されているため、新しく書くスクリプトではこちらを使うのが望ましいとされている。
システムの根幹に関わる launchd の操作は慎重に行う必要がある。特に /Library/LaunchDaemons/ や /System/Library/ 以下のファイルを扱う際は、誤った操作がシステム全体に影響を及ぼす可能性があるため、十分に注意して作業を進めることが重要である。
出典
- Stack Overflow: launchd: Confusion on the sematics of bootstrap/bootout for controlling services on macOS
- Ask Different: launchd: Confusion on semantics of bootstrap and bootout etc. after reading manual pages
- Alan Siu's Blog: launchctl "new" subcommand basics for macOS
- SS64.com: launchctl Man Page - macOS
- The Terminal Blogger: Dear Launchctl, We're All Using You Wrong
- Babo D's Corner: Launchctl 2.0 Syntax