macOS launchctl の基本 - launchd でジョブを動かす(plist と新旧コマンド)

macOS launchctl の基本 - launchd でジョブを動かす(plist と新旧コマンド)

作成日:
読了:12
更新日:

「Mac で定期実行したい」「常駐プロセスを自動起動したい」——cron ではなく launchd / launchctl の出番です。ただ launchctl は新旧2系統のコマンドがあって混乱しがち。この記事では、plist の書き方新構文(bootstrap 系)を中心に、基本を整理します(man・Apple 公式を一次ソースに確認)。

launchd / launchctl とは

  • launchd: macOS の統合サービス管理(Tiger / 10.4〜、PID 1)。従来の init・cron・xinetd 等を置き換える存在
  • launchctl: その launchd を操作する CLI
  • 基本はオンデマンド起動。条件(ログイン・時刻・間隔など)に応じてジョブを起動します

LaunchAgents と LaunchDaemons

まず「Agent か Daemon か」を決めます。これで置き場所と権限が変わります。

種別置き場所起動権限
ユーザー Agent~/Library/LaunchAgentsログイン時ログインユーザー(GUI可)
全ユーザー Agent/Library/LaunchAgents各ユーザーのログイン時ログインユーザー
Daemon/Library/LaunchDaemonsブート時(ログイン不要)rootUserName で変更可)
OS 管理/System/Library/...(触らない)SIP で保護
  • GUI を使う・ユーザー権限でよい・自分のログイン中だけAgent
  • ログイン前から・root で・常時Daemon

plist の基本

ジョブは plist(XML)で定義します。主なキーは次のとおり。

キー説明
Label(必須)一意な識別子。逆ドメイン記法(com.example.foo
ProgramArguments(必須)実行ファイル+引数の配列(第1要素が実行パス)
RunAtLoad読み込み時に即実行
StartIntervalN秒ごとに実行
StartCalendarIntervalcron 的な時刻指定
KeepAlive常駐・自動再起動
StandardOutPath / StandardErrorPathログ出力先
EnvironmentVariables環境変数

最小例(ユーザー Agent):

~/Library/LaunchAgents/com.example.myjob.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.example.myjob</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/local/bin/myscript.sh</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>StandardOutPath</key>
  <string>/tmp/myjob.out</string>
  <key>StandardErrorPath</key>
  <string>/tmp/myjob.err</string>
</dict>
</plist>

定期実行・常駐の指定

5分ごと
<key>StartInterval</key>
<integer>300</integer>
毎日3:55(cron 的)
<key>StartCalendarInterval</key>
<dict>
  <key>Hour</key><integer>3</integer>
  <key>Minute</key><integer>55</integer>
</dict>
常駐(クラッシュ時のみ再起動)
<key>KeepAlive</key>
<dict>
  <key>SuccessfulExit</key><false/>
</dict>

StartCalendarIntervalMinute / Hour / Day / Weekday / Month を指定でき、省略したキーはワイルドカード。スリープ中に来た時刻は、次回起動時に1回だけ実行されます(cron 同様、溜まって連発はしない)。

新コマンド(bootstrap 系)と旧コマンド

macOS 10.10 以降はドメイン指定の新構文が推奨です(旧 load/unload も動きますが非推奨)。

  • ドメイン: Agent は gui/$(id -u)、Daemon は system(sudo 必要)
操作新(推奨)旧(legacy)
読み込みlaunchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/foo.plistlaunchctl load -w foo.plist
解除launchctl bootout gui/$(id -u)/com.example.foolaunchctl unload -w foo.plist
即時起動/再起動launchctl kickstart -k gui/$(id -u)/com.example.foolaunchctl start com.example.foo
有効化/無効化launchctl enable|disable gui/$(id -u)/com.example.fooload -w / unload -w 相当
状態確認launchctl print gui/$(id -u)/com.example.foolaunchctl list | grep foo
一覧launchctl listlaunchctl list

Daemon の場合は sudosystem ドメイン:

Daemon の読み込み/解除/確認
sudo launchctl bootstrap system /Library/LaunchDaemons/com.example.foo.plist
sudo launchctl bootout    system/com.example.foo
sudo launchctl print      system/com.example.foo

実用フロー

作成 → 検証 → 配置 → 読み込み → 確認
# 1. plist を検証(構文チェック)
plutil -lint ~/Library/LaunchAgents/com.example.myjob.plist
 
# 2. パーミッション(Agentは644・自分の所有でOK)
chmod 644 ~/Library/LaunchAgents/com.example.myjob.plist
 
# 3. 読み込み
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.myjob.plist
 
# 4. 確認
launchctl print gui/$(id -u)/com.example.myjob
launchctl list | grep myjob

NOTE

Daemon(/Library/LaunchDaemons)は所有者 root:wheel・パーミッション 644が必要です(グループ/他者に書き込み権があると拒否されます)。sudo chown root:wheel ... && sudo chmod 644 ... を忘れずに。

plist を変更したら「bootout → bootstrap」

変更の反映(再読み込み)
launchctl bootout    gui/$(id -u)/com.example.myjob
launchctl bootstrap  gui/$(id -u) ~/Library/LaunchAgents/com.example.myjob.plist

WARNING

plist を編集しただけ・bootstrap し直しただけでは反映されません。 一度 bootout してから bootstrap し直すのが鉄則です。また disable 状態だと bootstrap しても起動しないので、その場合は先に enable します。

よくある落とし穴

  • 絶対パス必須: launchd の PATH は最小限。スクリプト内のコマンドもフルパスで書く(WorkingDirectory も絶対パス)
  • ログ出力先を指定しないと消える: StandardOutPath / StandardErrorPath を必ず設定。デバッグの命綱
  • Daemon にユーザー環境は無い: HOME や dotfiles、PATH は引き継がれない。必要なら EnvironmentVariables で明示
  • Label の重複禁止: 重複するとロード失敗
  • すぐ終わるプロセスは再起動が抑制される: 既定の ThrottleInterval(約10秒)より短時間で終了し続けると、launchd がクラッシュ扱いにして再起動を保留
  • SIP 保護領域(/System/Library/...)は触らない
  • launchctl print の出力はスクリプトでパースしない(Apple がリリースごとに変わりうると明言)

まとめ

  • まず Agent(ログイン時・ユーザー)か Daemon(起動時・root)かを決め、対応する場所に plist を置く
  • plist は LabelProgramArguments が基本。定期実行は StartCalendarInterval / StartInterval、常駐は KeepAlive
  • コマンドは新構文(bootstrap / bootout / kickstart / printを使う(旧 load/unload は非推奨だが動く)
  • 流れは 作成 → plutil -lint → 配置 → bootstrapprint/log。変更時は bootoutbootstrap
  • ハマりどころは 絶対パス・ログ出力先・最小PATH・10秒スロットル

cron に慣れていると最初は戸惑いますが、plist + bootstrap/bootout の型さえ掴めば、Mac の定期実行・常駐は launchd で完結します。ちなみに筆者はこの検証を tmux のペインから叩いていました(tmux のペイン位置の指し方)。シェルスクリプト側の安全策はset -euo pipefailも合わせてどうぞ。

参考リンク

Gemini CLI 終了、Antigravity CLI へ移行 - 何が変わり、何に注意すべきか

Gemini CLI 終了、Antigravity CLI へ移行 - 何が変わり、何に注意すべきか

9

Google が2026年6月18日に Gemini CLI と Gemini Code Assist IDE 拡張(AI Pro/Ultra/無料ユーザー向け)の提供を終了し、後継の Antigravity CLI(コマンド agy)へ移行を促しています。停止対象と例外、クローズドソースの Go 製マルチエージェント CLI への変化、MCP 設定の url→serverUrl リネーム、クォータの大幅縮小、CI/CD が壊れる注意点、移行手順までを Google 公式情報を一次ソースに整理します。

OpenCode 入門 - モデル非依存のオープンソース AI コーディングエージェント

OpenCode 入門 - モデル非依存のオープンソース AI コーディングエージェント

10

OpenCode を整理します。Anomaly(旧 SST)が開発する MIT ライセンスのオープンソース AI コーディングエージェントで、ターミナル(TUI)を中心にデスクトップ・IDE でも動きます。最大の特徴は models.dev 連携による「モデル非依存」で、Anthropic・OpenAI・Google・ローカルモデルなど多数のプロバイダを切り替えられること。インストール、opencode.json による設定、エージェントと権限、MCP 連携、Claude Code / Codex CLI との違いまで、公式ドキュメントを一次ソースにまとめます。