launchdとは|読み方とMacで定期実行する使い方
「launchdって何と読むの?」「Macで毎朝決まった時刻にスクリプトを動かしたいけど、cronでいいの?」——Macで何かを自動で動かそうとして調べ始めたとき、最初にこの名前でつまずく方は多いはずです。
結論から言うと、launchd(ローンチディー)はmacOSに最初から入っている「プログラムを起動・管理する仕組み」で、Macで定期実行するならApple自身がcronではなくlaunchdを勧めています。設定ファイル(plist)を1枚書いて決まった場所に置き、launchctl コマンドで登録すれば動きます。
株式会社Fyveでは、常時起動のMac mini 1台でSEO記事の執筆やSNS投稿、検索データの集計など11本の定期ジョブをlaunchdで回しています。この記事では、launchdの読み方と仕組み、cronとの違い、最小の設定手順、そして実際に運用してつまずいた点を、私たちの設定の実物を見せながら解説します。
launchdとは|読み方とmacOSでの役割
launchdは、macOSの起動時にカーネルから最初に呼ばれるプロセスで、そのあと動くほぼすべての常駐プログラムやバックグラウンド処理の起動・停止・再起動を管理しています。Appleのマニュアル(man launchd)では「System wide and per-user daemon/agent manager」(システム全体とユーザーごとのデーモン・エージェントの管理者)と説明されています。
私たち利用者から見ると、launchdは「この時刻に」「何秒ごとに」「ログインしたら」「落ちたら再起動して」といった条件でプログラムを動かしてくれる係です。Linuxでいえばsystemdとcronを合わせたような立ち位置になります。
launchdの読み方は「ローンチディー」
Appleは読み方を公式に示していませんが、一般には「ローンチディー」と読みます。前半は英語の launch(起動する)、末尾の d は daemon(デーモン)の d です。UNIXでは裏で動き続けるプログラムの名前の最後に d を付ける慣習があり、sshd(SSHの受け口)や httpd(Webサーバー)と同じ付け方です。
あわせて覚えておくと楽な用語が3つあります。
- launchctl(ローンチコントロール): launchdに指示を出すためのコマンド。登録・解除・即時実行・状態確認をこれで行う
- plist(ピーリスト): property list の略。ジョブの中身と条件を書くXML形式の設定ファイル
- ジョブ: launchdに登録した1つの仕事の単位。plist 1枚=ジョブ1つ
launchdはいつからある?
launchdは2005年のMac OS X 10.4 Tigerで導入されました。それまでmacOSにあった init、cron、inetd などの起動の仕組みを1つにまとめるために作られたものです。Appleの開発者向けドキュメント「Scheduling Timed Jobs」には、次のようにはっきり書かれています。
The preferred way to add a timed job is to use launchd.(時刻指定のジョブを追加する推奨の方法はlaunchdを使うことです)
Although it is still supported, cron is not a recommended solution. It has been deprecated in favor of launchd.(cronはまだ使えますが推奨されません。launchdのために非推奨になっています)
つまり、Macで「毎日○時に動かす」を作るなら、Appleの想定する正解はlaunchdです。
launchdとcronの違い
「cronなら書き方を知っているのに」という方のために、違いを表にまとめます。いちばん大きいのはスリープしていた間の扱いです。
比べる点 | launchd | cron |
|---|---|---|
Appleの位置づけ | 推奨 | 非推奨(動くが勧めない) |
設定の書き方 | ジョブごとにplist(XML)を1枚 | crontabに1行ずつ |
スリープ中に予定時刻が来た | 起きたときに実行(StartCalendarIntervalの場合) | 実行されない |
電源オフ中に予定時刻が来た | 実行されない(次の予定時刻まで待つ) | 実行されない |
時刻以外の起動条件 | ログイン時・一定間隔・ファイル変更・常駐(落ちたら再起動)など | 時刻のみ |
ログの出し先 | plistで標準出力・エラーの書き出し先を指定 | メールやリダイレクトで自前で用意 |
状態の確認 |
| ログを見るしかない |
書く量はcronのほうが少なく済みます。それでも私たちがlaunchdを選んでいるのは、ノートPCや省電力設定のMacで「寝ている間の予定が消えない」ことと、最後にいつ・どう終わったかをコマンド1つで確かめられることが、無人で回すうえで効くからです。
デーモンとエージェント|plistの置き場所
launchdのジョブには「デーモン」と「エージェント」の2種類があります。違いは誰の権限で、いつ動くかです。
- デーモン(Daemon): システム全体で1つだけ動く。ログインしていなくても動く。管理者権限が要る
- エージェント(Agent): ユーザーごとに動く。そのユーザーがログインしている間だけ動く
どちらになるかは、plistを置く場所で決まります。
置き場所 | 種類 | 誰が置くか |
|---|---|---|
| エージェント(自分のユーザー) | 自分 ← 個人の自動化はここ |
| エージェント(全ユーザー) | 管理者 |
| デーモン | 管理者 |
| エージェント | macOS(触らない) |
| デーモン | macOS(触らない) |
自分のスクリプトを自分の権限で動かしたいだけなら、~/Library/LaunchAgents 一択です。私たちの11本もすべてここにあります。ここに置いたplistは、ログインのたびに自動で読み込まれます。

実例|Mac mini 1台でlaunchdが回している11本のジョブ
抽象的な説明だけでは使いどころが見えにくいので、私たちが自動化用に常時起動しているMac miniで今動いている設定をそのまま紹介します。2026年10月19日時点で ~/Library/LaunchAgents に登録しているジョブは次のとおりです。
ジョブ | 起動の条件 | 書き方 |
|---|---|---|
SEO記事の執筆・公開 | 毎日 6:00 / 14:00 / 22:00 | StartCalendarInterval(配列で3つ) |
AIニュースのX投稿 | 毎日 7:00 / 12:00 / 19:00 | StartCalendarInterval(配列で3つ) |
検索需要の急増を見張る | 毎日 4:00 / 7:40 | StartCalendarInterval(配列で2つ) |
発信ネタの在庫づくり | 毎日 5:00 | StartCalendarInterval |
Substackのノート予約 | 毎日 8:37 | StartCalendarInterval |
開発ツールの更新まとめ | 毎日 9:00 | StartCalendarInterval |
注目リポジトリの紹介 | 毎日 20:00 | StartCalendarInterval |
Xの話題のまとめ | 毎日 21:00 | StartCalendarInterval |
検索データの週次集計 | 毎週月曜 4:00 | StartCalendarInterval(Weekday=1) |
新モデルの記事一式の発注確認 | 30分ごと | StartInterval(1800秒) |
監視用のターミナルを開く | ログイン時に1回 | RunAtLoad |
このほかに、いまは止めているジョブが4本あります。止め方は後ほど説明しますが、私たちはplistの名前の末尾に .disabled を付けて、ログイン時に読み込まれないようにしています。消さずに残しておけば、再開するときに名前を戻すだけで済みます。
時刻で決めた起動が1日13回、30分ごとの確認が1日48回あり、これを1台のMacに任せていますが、launchdそのものが原因で止まったことは一度もありません。止まった原因は、後で紹介するとおり、どれも「launchdから見た環境」と「ターミナルで試した環境」の違いでした。

時刻を見ると、8:37のように中途半端な分があります。これは他のジョブとわざと時刻をずらしたものです。時刻が重なったときの扱いも後の節で触れます。
launchdで定期実行する使い方|最小の5ステップ
ここからは、毎朝7時にスクリプトを1本動かすジョブを、ゼロから作って登録するまでの手順です。ターミナルを開いて順に進めれば、10分ほどで終わります。
ステップ1:動かすスクリプトを用意する
まず、ターミナルから手で実行して正しく動くスクリプトを用意します。置き場所はホームの下の、デスクトップや書類フォルダ以外にしておくのが無難です(理由は「つまずきやすい点」で説明します)。
mkdir -p ~/scripts
cat > ~/scripts/hello.sh <<'EOF'
#!/bin/bash
echo "$(date '+%Y-%m-%d %H:%M:%S') hello from launchd"
EOF
chmod +x ~/scripts/hello.sh
~/scripts/hello.sh # 手で一度動かして確認ステップ2:plistを書く
次に、ジョブの中身と条件を書いたplistを ~/Library/LaunchAgents に作ります。ファイル名は「ラベル名.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.hello</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/Users/あなたのユーザー名/scripts/hello.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key><integer>7</integer>
<key>Minute</key><integer>0</integer>
</dict>
<key>StandardOutPath</key>
<string>/Users/あなたのユーザー名/Library/Logs/hello.log</string>
<key>StandardErrorPath</key>
<string>/Users/あなたのユーザー名/Library/Logs/hello.log</string>
</dict>
</plist>使っているキーは5つだけです。
キー | 意味 | 必須か |
|---|---|---|
Label | ジョブの名前。他と重ならない文字列(逆ドメイン形式が慣習) | 必須 |
ProgramArguments | 実行するコマンドと引数を1つずつ並べた配列 | 必須(Programキーを使わない場合) |
StartCalendarInterval | 実行する時刻。省略した項目は「毎」扱い | 任意 |
StandardOutPath | 標準出力の書き出し先 | 任意(実質必須) |
StandardErrorPath | エラー出力の書き出し先 | 任意(実質必須) |
注意点は、パスに ~ を使わず、フルパスで書くことです。plistの中ではシェルが ~ をホームに展開してくれません。また ProgramArguments は「コマンド文字列」ではなく「単語ごとの配列」なので、/bin/bash hello.sh を1つの string に書くと動きません。Appleのマニュアルにも「Many people are confused by this key(このキーで混乱する人が多い)」と注意書きがあります。
ステップ3:書き間違いがないか検査する
XMLの閉じ忘れなどがあると、登録の段階で分かりにくいエラーになります。先に plutil で検査しておきます。
plutil -lint ~/Library/LaunchAgents/com.example.hello.plist
# → OK と出ればよいステップ4:launchctlで登録する(bootstrap)
plistを置いただけでは、次にログインするまで読み込まれません。今すぐ有効にするには launchctl bootstrap で登録します。
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.hello.plistgui/$(id -u) は「今ログインしている自分のユーザーの領域」という意味です。id -u が自分のユーザー番号を返すので、このままコピーして使えます。
ステップ5:すぐに1回動かして確認する(kickstart)
翌朝7時まで待たずに、今すぐ1回動かして確かめます。
launchctl kickstart -k gui/$(id -u)/com.example.hello
cat ~/Library/Logs/hello.log # 出力が書かれていれば成功登録の状態は launchctl print で見られます。私たちが日常的に見ているのは「runs(起動された回数)」と「last exit code(最後の終了コード)」の2行です。
launchctl print gui/$(id -u)/com.example.hello | grep -E 'state|runs|last exit code'
# state = not running
# runs = 1
# last exit code = 0 ← 0 なら正常終了一覧でざっと見たいときは launchctl list | grep example も使えます。左から「実行中ならPID」「最後の終了コード」「ラベル」が並びます。
止める・設定を変えるとき(bootout)
止めるときは bootout で登録を外します。plistを書き換えたときも、一度外してから登録し直さないと変更が反映されません。
launchctl bootout gui/$(id -u)/com.example.hello # 外す
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.hello.plist # 登録し直すずっと止めておきたい場合は、外したうえでplistを別の名前に変えるか、別の場所に移します。私たちが .disabled を付けているのはこのためです。ファイルが ~/Library/LaunchAgents に「○○.plist」として残っている限り、次のログインでまた読み込まれます。
load・unload と bootstrap・bootout の違い
検索すると launchctl load ~/Library/LaunchAgents/...plist という書き方がよく出てきます。これは今も動きますが、man launchctl では「LEGACY SUBCOMMANDS(古いサブコマンド)」の扱いで、代わりに bootstrap・bootout・enable・disable を使うよう案内されています。
やりたいこと | 今の書き方 | 古い書き方 |
|---|---|---|
登録する |
|
|
外す |
|
|
すぐ1回動かす |
|
|
状態を見る |
|
|
古い書き方は、失敗しても分かりやすいエラーを返さないことがあります。新しく書くなら今の書き方にそろえておくほうが、つまずいたときに原因を追いやすくなります。
launchdのスケジュールの書き方|StartCalendarIntervalとStartInterval
定期実行の条件は、主に次の2つのキーで書きます。
- StartCalendarInterval: 「毎日7時」「毎週月曜4時」のように時刻で指定する。cronに近い
- StartInterval: 「1800秒ごと」のように間隔で指定する
StartCalendarIntervalの書き方の例
使える項目は Minute(0〜59)・Hour(0〜23)・Day(1〜31)・Weekday(0〜7、0と7が日曜)・Month(1〜12)です。書かなかった項目は「毎」扱いになります。
やりたいこと | 書き方 |
|---|---|
毎日7:00 |
|
毎時0分 |
|
毎週月曜4:00 |
|
毎月1日9:00 |
|
1日に3回(6時・14時・22時) |
|
1日に複数回動かしたいとき、cronなら「0 6,14,22 * * *」と1行で書けますが、launchdでは時刻ごとに dict を作って配列に並べます。私たちのSEOのジョブは実際にこう書いています。
<key>StartCalendarInterval</key>
<array>
<dict><key>Hour</key><integer>6</integer><key>Minute</key><integer>0</integer></dict>
<dict><key>Hour</key><integer>14</integer><key>Minute</key><integer>0</integer></dict>
<dict><key>Hour</key><integer>22</integer><key>Minute</key><integer>0</integer></dict>
</array>よくある間違いが、Minute を書き忘れて Hour=7 だけにするものです。Minute を省くと「毎分」扱いになり、7:00〜7:59の間に毎分起動されます。時刻で指定するときは Minute を必ず書いてください。
StartIntervalの書き方と注意点
「30分ごと」のように間隔で動かすなら StartInterval に秒数を書きます。
<key>StartInterval</key>
<integer>1800</integer>ただし、man launchd.plist には2つの注意が書かれています。1つは、スリープ中に来た回は飛ばされること。もう1つは、前の回がまだ動いている間に次の回が来たら、その回も飛ばされることです。私たちの30分ごとのジョブは「新しい発注が来ていないか確認するだけ」の軽い処理なので、1回飛んでも次の30分後に拾えれば困りません。逆に、決まった時刻に必ず1回動いてほしい処理は StartCalendarInterval で書くのが安全です。
スリープ中・電源オフ中に予定時刻が来たらどうなるか
launchdを選ぶ理由になりやすい部分なので、図にまとめました。

- StartCalendarInterval × スリープ中: 起きたときに実行される。寝ている間に複数回の予定が過ぎていても、起きたときにまとめて1回だけ実行される
- StartCalendarInterval × 電源オフ中: 実行されない。次の予定時刻まで待つ
- StartInterval × スリープ中: その回は飛ばされる
- cron × スリープ中・電源オフ中: どちらも実行されない
「まとめて1回」は見落としやすい点です。ノートPCを夜に閉じて朝開いたとき、0時・3時・6時の3回分が溜まっていても、走るのは1回だけです。回数を数えて処理するスクリプトでは、この前提で作っておく必要があります。
launchdでつまずきやすい5つの点|実運用で起きたこと
ここからが、実際に運用して私たちがつまずいた点です。どれも「ターミナルで手で動かすと動くのに、launchdから動かすと動かない」という同じ形で現れます。
1. PATHやシェルの設定が読まれない
launchdから起動したプログラムは、ターミナルのように ~/.zshrc などのシェル設定を読みません。そのため、Homebrewや自分で入れたコマンドが「command not found」になります。
私たちはジョブを起動する共通のシェルスクリプトの先頭で、PATHを明示的に書いています。
export PATH="$HOME/.local/bin:/usr/local/bin:/usr/bin:/bin"同じ理由で、python3 のようにバージョンが複数入りうるコマンドも要注意です。私たちのMac miniでは、ターミナルでは新しいPythonが動くのに、launchdから呼ぶとmacOS標準の古いPython 3.9が使われ、新しい書き方を使ったスクリプトが落ちたことがありました。今は使いたいPythonをフルパスで呼ぶようにしています。
環境変数(APIキーなど)も同じで、シェル設定に書いたものは渡りません。plistの EnvironmentVariables キーに書くか、スクリプトの中で設定ファイルを読み込みます。キーをplistに直接書くとファイルを見せただけで漏れるので、私たちはスクリプトの中で別ファイルから読み込む形にしています。
2. 「Operation not permitted」で止まる(プライバシー保護)
macOSは、デスクトップ・書類・ダウンロードなどのフォルダへのアクセスを、アプリやプログラムごとに許可制にしています。ターミナルには許可を出していても、launchdから起動したスクリプトには許可が無い、ということが起きます。すると「Operation not permitted」で止まります。
私たちはgitの自動コミットをlaunchdで回していて、これが原因で18日間コミットが止まっていたのに気づかなかったことがあります。対処は、スクリプトをデスクトップや書類フォルダの外に置くか、「システム設定 → プライバシーとセキュリティ → フルディスクアクセス」で実行するプログラム(例: /bin/bash)を許可することです。経緯は次の記事にまとめています。
3. 終了コード78(EX_CONFIG)で起動しない
launchctl print の last exit code が 78 になっているときは、ジョブの中身が動く前に、launchd側の設定で失敗しています。私たちの場合は、ログの書き出し先に指定したフォルダが存在しない・書き込めないことが原因でした。StandardOutPath の親フォルダが実在するかを最初に確かめると早く片付きます。
終了コードの読み方の目安は次のとおりです。
last exit code | 意味の目安 | 最初に見るところ |
|---|---|---|
0 | 正常終了 | — |
1・2 など | スクリプト自体がエラーで終わった | StandardErrorPath のログ |
78 | launchd側の設定の問題(起動前に失敗) | ログの出力先・パスの実在・権限 |
126・127 | 実行権限が無い・コマンドが見つからない | chmod +x とフルパス、PATH |
4. 時刻の重なりで処理がぶつかる
launchdは同じジョブを二重には起動しませんが、別のジョブ同士が同じ時刻に動くことは止めません。私たちのジョブはどれも同じ作業フォルダを読み書きするため、2本が同時に動くと、片方の書きかけのファイルをもう片方が消してしまう危険がありました。
対処として、ジョブを起動する共通スクリプトに「1本ずつ順番に動かす」仕組みを入れています。macOSにはLinuxの flock コマンドが標準で入っていないので、mkdir がすでにあるフォルダを作れずに失敗する性質を使った簡単なロックです。
while ! mkdir "$HOME/.myjob.lock" 2>/dev/null; do
sleep 30 # 先に動いているジョブが終わるのを待つ
done
trap 'rm -rf "$HOME/.myjob.lock"' EXIT # 終わったら必ず外す実際には、ロックを持ったまま異常終了したジョブを見分けて奪い取る処理と、50分待っても空かなければ諦めて通知する処理も足しています。これで「時刻が重なる」は「少し待たされる」に変わり、時刻表をそこまで神経質に組まなくてよくなりました。それでも8:37のように分をずらしているのは、待ち時間を減らすためです。
5. スリープ・停電で予定が消える
前の節のとおり、StartCalendarInterval はスリープ中の分を起きたときに拾ってくれますが、電源が切れている間の分は拾いません。無人で回すMacは、そもそも眠らせないほうが確実です。私たちのMac miniは次の設定にしています。
pmset -g | grep -E ' sleep|autorestart'
# sleep 0 ← システムはスリープしない
# autorestart 1 ← 停電から復旧したら自動で起動するあわせて「システム設定 → ユーザとグループ」で自動ログインを有効にしています。~/Library/LaunchAgents のエージェントはログインしている間だけ動くので、停電後に再起動してもログイン画面で止まっていればジョブは動きません。ここまでそろえて、ようやく「置いておけば勝手に動く」状態になります。常時起動のMacをサーバーとして使う全体の組み方は、こちらで紹介しています。
Claude Codeの定期実行機能とlaunchdの使い分け
AIエージェントを定期的に動かしたい場合、launchd以外にも選択肢があります。Claude Codeにはセッションの中で繰り返す /loop や、PCを閉じていても動くクラウドのroutinesがあります。
私たちの使い分けは単純です。
- Macの中のファイルや認証を使う・毎日決まった時刻に動かす → launchd(私たちのジョブはすべてこれ)
- 作業中のセッションで、数分おきに様子を見る →
/loop - Macの電源と関係なく動かしたい・手元のファイルを使わない → クラウドのroutines
launchdは「そのMacの中のもの」を使えるのが強みで、そのMacが止まれば一緒に止まるのが弱みです。機能ごとの違いは次の記事で一覧にしています。
launchdについてよくある質問
Q. launchdは何と読みますか?
「ローンチディー」と読むのが一般的です。launch(起動する)と、常駐プログラムを表す daemon の d を合わせた名前です。
Q. Macでcronはもう使えないのですか?
今も使えます。ただしAppleは非推奨としており、スリープ中に来た予定は実行されません。新しく作るならlaunchdを勧めます。
Q. Macを再起動したら、登録したジョブは消えますか?
~/Library/LaunchAgents にplistが残っていれば、ログイン時に自動で読み込まれるので消えません。逆に、bootout で外しただけでplistを残しておくと、次のログインで復活します。なお launchctl print の runs(起動回数)は再起動で数え直しになります。
Q. plistを書き換えたのに反映されません。
launchdは登録した時点の内容で動いています。bootout で外してから bootstrap で登録し直してください。
Q. 画面のある設定ツールはありますか?
macOS標準ではありません。市販のGUIツールもありますが、中身はこの記事で紹介したplistを作っているだけです。ジョブが数本のうちは、plistを直接書いて launchctl print で確かめるほうが、何が起きているかを把握しやすいと私は考えています。
まとめ|launchdは「Macに置いた仕事を時刻どおりに動かす係」
- launchd(ローンチディー)はmacOSのプログラム起動・管理の仕組みで、Appleはcronではなくlaunchdでの定期実行を推奨している
- 個人の自動化は
~/Library/LaunchAgentsにplistを置き、launchctl bootstrapで登録、kickstartで試し、printで終了コードを確かめる - 時刻で動かすなら StartCalendarInterval(Minuteの書き忘れに注意)。スリープ中の予定は起きたときにまとめて1回実行される
- つまずく原因の大半は「ターミナルとlaunchdで環境が違う」こと。PATH・プライバシー保護・ログの出力先・時刻の重なり・スリープの5点を先に潰しておく
株式会社Fyveでは、このlaunchdの上で、時刻で決めたAIの仕事を毎日13回、無人で回しています。最初の1本は、毎朝ログに1行書くだけのジョブで十分です。それが動いたら、次に任せたい仕事を1つずつ載せていくのが、遠回りに見えていちばん早い進め方です。
Claude Codeを「素のまま」使うな

設定で差がつく——CLAUDE.md・権限・スキルの実物を公開(全24ページ)
素のClaude Codeは"優秀な新入社員"。仕事を教えるほど、自分専用になります。覚えさせる4点セット——会社の説明書(CLAUDE.md)・権限の柵・手順書(スキル)・フォルダの地図——を、1人会社の実運用からコピペで使える型つきで公開します。
- そのまま書き換えて使えるCLAUDE.mdの型
- お金と送信をAIに触らせない「3段階の柵」
- 1回教えたら何度でも動く、手順書のコピペ雛形
- AIが迷子にならないフォルダ構造の3原則
メールアドレス登録で他にも様々な資料を閲覧できます








+8PDF 17点・合計440ページ + すぐ使えるzip素材 3点
どれも登録後の受け取りページから、まとめてダウンロードできます。
毎週配信の無料ニュースレター「AIネイティブ超研究」の購読特典です。メール登録後すぐ、受け取りページのご案内が届きます。そこにはこの資料に加えて、過去の特典もすべてまとめて置いてあります。あわせて、AI活用に関するお知らせやお役に立てそうなご案内をお送りすることがあります。解除はいつでも1クリック。
御社の業務に合わせたClaude Code導入支援
「AIツールを導入したが、現場で使われない」を終わらせる。
業務課題のヒアリングから設計、ハンズオン実践、運用定着まで一貫して支援します。