最終更新: 2026-04-28
何が起きたか
Ollama を launchd で常駐させてから、作業中に Mac の風切り音 Python スクリプト常駐化の環境設定チェックリストが急に大きくなり始めた。Activity Monitor を開くと ollama_llama_server が CPU 80~110% を常時食っている。モデルを使っていないのに。私は「KeepAlive=true にしとけば楽」と思って設定した plist が、逆に Mac を常時フル稼働状態にしていたことに気づくまで2日かかった。その間、ファンは24時間回り続けた。
環境
- macOS 15.4 Sequoia(MacBook Pro M5 32GB)
- Ollama 0.6.x(Homebrew インストール済み)
- qwen3.5:9b / gemma4:26b をメインモデルとして Ollama モデルの性能比較と選定ガイド使用
- launchd plist を
~/Library/LaunchAgents/に配置 - 自動化スクリプト:
01_Scripts/lib/ai_client.py経由で各パイプラインが呼び出し
詰まったポイント
最初に疑ったのはスクリプト側だった。ai_client.py がループしてるとか、パイプラインが暴走してるとか。でも全部の Python プロセスを止めてもファンは回り続けた。
私は ps aux | grep ollama で確認すると、ollama_llama_server が生きていた。Ollama はモデルをリクエストされた瞬間にサーバープロセスを起動して、デフォルトで 5分間メモリに保持する仕様だ。問題はそこじゃなくて、保持が終わった後に launchd が即座にプロセスを再起動していたこと——これが原因だった。再起動のたびにモデルの再ロードが走る。その分の CPU 消費が止まらない。
KeepAlive=true の意味を理解していなかった。「プロセスが落ちたら即座に再起動する」という設定なので、Ollama がモデルをアンロードしてプロセスを終了しようとすると、launchd が黙って復活させる。その際にモデルの再ロードが走り、CPU 使用率が上がり、プロセスが重くなり……ループだ。
エラーログは特に出ない。/usr/local/var/log/ にも ~/Library/Logs/ にも何も残っていなかった。「正常に動いているけど重い」という状態で、デバッグの手がかりが掴みにくかった。syslog を見ても launchd の再起動記録は見つからず——結局、1日目は完全に暗中模索。
解決までの手順
ステップ1: 現状の plist を確認する
まず私は以下を実行した。
cat ~/Library/LaunchAgents/com.taito.ollama.plist
中身は KeepAlive=true・RunAtLoad=true のシンプルな構成だった。モデルが自動アンロードされてプロセスが終了するたびに launchd が再起動する仕組み。この時点で仮説が固まった。
ステップ2: OLLAMA_KEEP_ALIVE を短くセットして様子を見る
Ollama にはモデルの保持時間を制御する環境変数がある。デフォルトは 5m(5分)。私はこれを 0 にしてリクエスト直後に即座に解放させてみた。
launchctl unload ~/Library/LaunchAgents/com.taito.ollama.plist
OLLAMA_KEEP_ALIVE=0 ollama serve &
CPU 使用率が 80% から 15% に低下した。モデルが即アンロードされてプロセスが静かになっている。手応えあり——ここで確信が深まった。
ステップ3: KeepAlive を OnDemand 相当に切り替える
launchd の KeepAlive=true を削除して、RunAtLoad=false にした。Ollama は最初から起動しない。必要な時にスクリプト側から呼び出す構成に変える。この設定変更だけで、ファンの音が 2日ぶりに止まった。
ステップ4: 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.taito.ollama</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/ollama</string>
<string>serve</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>OLLAMA_KEEP_ALIVE</key>
<string>0</string>
</dict>
<key>RunAtLoad</key>
<false/>
<key>StandardErrorPath</key>
<string>/tmp/ollama.err.log</string>
<key>StandardOutPath</key>
<string>/tmp/ollama.out.log</string>
</dict>
</plist>
ステップ5: 検証と本番投入
私は以下で新しい plist を読み込んで、1週間の本番運用で確認した。
launchctl load ~/Library/LaunchAgents/com.taito.ollama.plist
その1週間、ファンは Ollama 呼び出し時にだけ回転し、用が済むと止まるようになった。CPU は常に 5% 以下。メモリ不足のアラートも消えた。
試してわかったこと
KeepAlive は「常に生きてることを保証する」という意味で、単なる再起動フラグではない。launchd の仕組みそのものを知らずに「常駐」の言葉だけで有効にしてしまった——これが落とし穴だった。
OLLAMA_KEEP_ALIVE=0 にすると、モデルがメモリから即座に消える。その後のリクエストでは再ロードが走るが、バックグラウンド稼働中の無意味な再起動・再ロードと比較すれば、無視できる程度の負荷。
ログが出ないプロセスの暴走は本当に厄介だ。Activity Monitor だけでなく、launchctl list と log stream --predicate 'process == "launchd"' を併用すると、launchd の動きが可視化できる。
まとめ
ローカルモデルを常駐させるときは、KeepAlive=true が本当に必要か立ち止まるべき。私の場合、オンデマンド起動で十分だった。マシンリソースが限られた環境ほど、この判断が効く。
次は Ollama の Modelfile に PARAMETER keep_alive を明示的に記述する方向で検討中。個別モデルごとに保持時間を制御できるから、自動化パイプラインとの相性がいい。