1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

launchdとn8nの使い分け — Macローカルとクラウドの自動化を3ヶ月運用した設計パターン

1
Last updated at Posted at 2026-04-09

日曜の朝、Chatworkの通知が来なかった。

毎朝9時に走るはずのスケジュール通知。月曜から土曜まで問題なく動いていた。原因は30秒でわかった。Macがスリープしていた。日曜は朝寝坊したからMacを開いていなかった。launchdで組んだバッチ処理は、Macが起きていなければ動かない。当たり前のことだが、それに気づくのは実際に落ちたときだ。

この一件で、自動化の設計を根本から見直した。「何をlaunchdで動かし、何をn8nで動かすか」——この判断基準が固まるまでに3ヶ月かかった。

前提:自分の環境

  • Mac(M2 MacBook Air): メインの開発・作業マシン。Claude Codeはここで動く
  • VPS(4コア6GB・Docker): n8nを含む15コンテナが24時間稼働
  • 自動化の総数: launchd 8本 + n8n 25本 + GASトリガー 12本

3つの実行基盤を使い分けている。この記事ではlaunchdとn8nに絞る。GASは守備範囲が違うので別の話だ。

launchdの得意なこと

ローカルファイルの操作

launchdが唯一無二の強みを持つのは、Macのローカルファイルを直接触れること。

<!-- ~/Library/LaunchAgents/com.team.auto-backup.plist -->
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.team.auto-backup</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/bash</string>
    <string>/Users/me/tools/auto-backup.sh</string>
  </array>
  <key>StartCalendarInterval</key>
  <array>
    <dict>
      <key>Hour</key><integer>12</integer>
      <key>Minute</key><integer>0</integer>
    </dict>
    <dict>
      <key>Hour</key><integer>23</integer>
      <key>Minute</key><integer>0</integer>
    </dict>
  </array>
</dict>
</plist>

たとえばローカルのgitリポジトリを1日2回自動コミットする処理。ファイルの差分を検出して、変更があればコミットする。これはMac上でしか動かない。VPSにリポジトリのクローンを置く方法もあるが、リアルタイムの作業ファイルを扱うならローカルに限る。

Obsidianのvaultをバックアップする処理も同じだ。ローカルのmdファイルを読んで、加工して、gitに入れる。この手の処理はlaunchdの独壇場。

Claude Codeとの連携

Claude Codeのセッション外で、ローカル環境を整備する処理もlaunchd向きだ。

たとえば、深夜にGemini CLIを使ってその日のメモを統合分析するスクリプト。ローカルのファイルを読んで、分析結果をローカルに書き出す。Macが起きてさえいれば、3時に勝手に動いて朝には結果が置いてある。

launchdでやるべきこと(まとめ)

  • ローカルファイルの読み書きが必要な処理
  • gitの自動コミット・push
  • ローカルの静的サイト生成
  • Claude Codeのセッション外で動くローカルスクリプト

launchdの不得意なこと

Macがスリープしたら終わり

冒頭の話に戻る。launchdはmacOSのデーモン管理システムだ。Macが起きていなければ動かない。

しかもlaunchdの StartCalendarInterval はスリープ中に予定時刻を過ぎた場合、復帰後にリカバリ実行されることもある。Apple公式ドキュメントには「コンピュータがスリープ中だった場合、起動後に実行される」と書いてある。だが実際には、スリープが長時間続いた場合やmacOSのバージョンによって挙動が安定しない。リカバリされないケースを何度も経験した。

[期待] 毎朝9:00に実行
[現実] 9:00にMacスリープ → 10:30に開く → 実行されない

StartInterval(秒数指定の繰り返し)の場合は、スリープ復帰後に1回だけリカバリ実行される——らしいのだが、これも環境によって挙動が違う。信じないことにしている。

外部APIへの依存

launchdからChatwork APIを叩くスクリプトを動かしていた時期がある。問題は2つ。

  1. Macがスリープしていたら叩けない(上述)
  2. Wi-Fiが切れていたらタイムアウトする

移動中にMacのWi-Fiが不安定になると、API呼び出しが中途半端に失敗する。レスポンスを受け取れずにプロセスが残る。ログに curl: (28) Operation timed out が並ぶ。

外部APIを定期的に叩く処理は、常時接続が保証されたサーバーで動かすべきだ。当たり前のことだが、最初は「Macでいいじゃん」と思っていた。甘かった。

エラー通知が面倒

launchdのジョブが失敗したとき、通知する仕組みは自分で作る必要がある。標準出力・標準エラーはログファイルに落ちるが、能動的に見に行かないと気づかない。

n8nなら、Error WFに飛ばしてChatworkに通知する仕組みが10分で組める。launchdでこれをやろうとすると、シェルスクリプト内にエラーハンドリングとcurl通知を自前で書くことになる。できるけど、毎回書くのか? という話だ。

n8nの得意なこと

24時間365日、止まらない

VPS上のDockerで動いているn8nは、Macのスリープに関係なく動き続ける。これが最大のメリット。

[launchd] Macが寝てたら → 止まる
[n8n]     VPSが生きてれば → 動く

夜間バッチ、休日の定期処理、5分おきのポーリング——「いつ動いても確実に動く」必要がある処理は全部n8n。

外部API連携が楽

n8nには主要サービスのノードが標準で用意されている。Chatwork、Slack、Gmail、Google Sheets、HTTP Request——ノードを置いて認証情報を入れるだけ。curlを手書きする必要がない。

しかもリトライロジックが組み込まれている。HTTP Requestノードには「失敗時にN回リトライ」の設定がある。launchdのシェルスクリプトでこれをやろうとすると:

MAX_RETRY=3
for i in $(seq 1 $MAX_RETRY); do
  response=$(curl -s -w "%{http_code}" ...)
  http_code="${response: -3}"
  if [ "$http_code" = "200" ]; then
    break
  fi
  sleep $((i * 5))
done

毎回これを書くのか。書かないだろう。n8nならチェックボックスを入れるだけだ。

エラーハンドリングが構造化されている

n8nには「Error Trigger」というノードがある。任意のWFがエラーで停止したとき、別のWFを起動できる。

うちでは全WF共通の「Error WF」を1本作っていて、どのWFが落ちてもChatworkに通知が飛ぶようにしている。WF名・エラー内容・発生時刻が整形されて届く。

{
  "workflow": "API監視Bot v2",
  "error": "HTTP 429 Too Many Requests",
  "timestamp": "2026-04-03T14:22:31Z"
}

これを一元化できるのが大きい。launchdだと、8本のスクリプトそれぞれにエラー通知ロジックを書く必要がある。1箇所直し忘れて、あるスクリプトだけ通知が来ないまま3日落ちてた、ということが実際にあった。

WFの視覚化とデバッグ

n8nのWebUIでは、実行結果がノードごとに色分けされる。成功=緑、失敗=赤、スキップ=グレー。どこで詰まったかが一目でわかる。

ターミナルのログを grep ERROR で漁る作業とは雲泥の差だ。

判断基準:3つの問い

「この処理、launchdとn8nのどっちで動かす?」

3ヶ月の運用で、判断基準が3つに収束した。

問い① ローカルファイルを触るか?

Yesならlaunchd。ローカルのgitリポジトリ、Obsidianのvault、Claude Codeのプロジェクトファイル——これらはMac上にしかない。VPSにはない。

ただし、「ファイルを読むだけ」ならrsyncやSCPでVPSに転送してn8nで処理する方法もある。判断基準は「リアルタイムのファイルが必要か」だ。5分前のファイルでよければVPS側で処理できる。

問い② 24時間動く必要があるか?

Yesならn8n。Macは寝る。人間も寝る。夜間バッチ、休日処理、5分おきのポーリングは全部n8n。

「平日の勤務時間だけでいい」なら、launchdでもいける。ただし「本当に勤務時間だけでいいか?」は慎重に考えるべきだ。最初は「平日だけ」と思っていたのに、あとから「やっぱり休日も」になるパターンを3回経験した。最初からn8nで組んでおけばよかった。

問い③ 外部APIを叩くか?

Yesならn8n。理由は前述の通り、常時接続・リトライ・エラー通知の3点セットがn8nには標準装備されている。launchdでcurlを手書きして同じ信頼性を出すのは、不可能ではないが割に合わない。

フローチャート

ローカルファイルを触る?
  → Yes → launchd
  → No ↓
24時間動く必要がある?
  → Yes → n8n
  → No ↓
外部APIを叩く?
  → Yes → n8n
  → No → どっちでもいい(n8n寄り)

「どっちでもいい」場合はn8nを推す。理由はエラー通知の一元化。launchdを増やすと監視の穴が増える。

実際の配置

参考までに、自分の環境での配置を書いておく。

launchd(8本)

ジョブ 頻度 理由
gitリポジトリの自動コミット 12:00 / 23:00 ローカルファイル操作
Obsidian vault バックアップ 23:30 ローカルファイル操作
ローカルログのローテーション 毎日4:00 ローカルファイル操作
深夜統合分析(Gemini CLI) 毎日3:00 ローカルファイル読み書き
その他4本 各種 ローカル依存

n8n(25本+)

WFカテゴリ 本数 理由
外部API監視・通知 8本 24時間 + 外部API
定期レポート生成 5本 24時間
Webhook受信・処理 4本 常時待受
エラーハンドリング 2本 全WF共通基盤
インフラ監視 3本 24時間
その他 3本+ 各種

見ての通り、本数はn8nが3倍以上。「迷ったらn8n」の結果、自然とこうなった。

失敗から学んだこと3つ

①「Macでいいじゃん」は甘い

最初はlaunchdで全部やろうとした。curlでAPIを叩き、jqでパースし、結果をファイルに書き出す。動く。動くのだが、Macをスリープした瞬間に全部止まる。金曜の夜にMacを閉じて月曜に開いたら、土日の処理が全部飛んでいた。

② launchdのログは能動的に見ないと気づかない

/tmp/com.team.*.log にログを吐くようにしていたが、3週間見ていなかった。見に行ったら、あるジョブが2週間前から毎回失敗していた。/tmp は再起動でクリアされるので、古いログは消えていた。ログの場所を /var/log/ に変えて、ログローテーションを入れた。

③ 2つの基盤の監視も自動化する

launchdのジョブが失敗してもn8nは知らない。n8nが落ちてもlaunchdは知らない。2つの基盤が独立しているので、片方が死んでいても気づかない。

解決策として、n8n側にインフラ全体のヘルスチェックWFを作った。15分おきにn8nの全WF + VPSの全コンテナの状態を確認し、異常があればChatworkに通知する。launchd側は日次でVPSに curl を飛ばして疎通確認する。お互いを監視する構造にした。

cronという第3の選択肢

VPS上にはcronもある。n8nとcronの使い分けも簡単に書いておく。

観点 cron n8n
設定方法 crontab -e WebUI or MCP
ロジック分岐 シェルで書く IFノードで
エラー通知 自前で書く Error WFに集約
視覚的デバッグ なし あり
APIリトライ 自前で書く チェックボックス

5分おきにCSVにデータを蓄積するだけの処理、APIを叩かないファイル整理——こういう「ロジックがないバッチ」はcronの方がシンプルだ。n8nのノードを組むまでもない。

ただ、cronが増えると「何がどこで動いているか」が見えなくなる。crontab -l の出力を定期的にバックアップしておくことを勧める。

設計パターンの結論

処理の性質 基盤 理由
ローカルファイル操作 launchd Mac上にしかないファイル
外部API定期実行 n8n 24時間 + リトライ + 通知
Webhook受信 n8n 常時待受
単純なファイルバッチ(VPS) cron ノードを組むまでもない
判断に迷ったら n8n 監視の一元化

3ヶ月前、日曜の朝に通知が来なかった。あのとき「launchdでいいじゃん」をやめた。正しい判断だったと思っている。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?