mlx-serveをMac Miniにインストールしてサービス化するメモ
Mac Mini (M4 Pro, 64GB)にremoteからsshでログインして、mlx-serveをインストール、サービス化するメモ
mlx-serveのインストール
brew trust ddalcu/mlx-serve
brew trust --cask ddalcu/mlx-serve/mlx-core
brew tap ddalcu/mlx-serve https://github.com/ddalcu/mlx-serve
brew install mlx-serve
$ mlx-serve --version
[mem] MLX buffer-pool cap 8192 MB (was 124518 MB)
mlx-serve 26.9.2
mlx 0.32.2
mlx-c 56b2d39fc831
nax off (requires M5-class GPU)
ggml 0.20.1 (60eeeb608)
llama.cpp b10472
gguf 3
ds4 unknown
Modelのpull
mlx-serve pull ddalcu/Qwen3.8-27B-MLX-Serve-8bit
mlx-serve pull ddalcu/Qwen3.8-27B-MLX-Serve-4bit
mlx-serveのサービス化
plistファイルの作成
ユーザー名はmakiの想定。
sudo tee /Library/LaunchDaemons/am.ik.mlx-serve.plist > /dev/null << 'EOF'
<?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>am.ik.mlx-serve</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/bin/mlx-serve</string>
<string>serve</string>
<string>--metrics</string>
</array>
<key>WorkingDirectory</key>
<string>/Users/maki</string>
<key>UserName</key>
<string>maki</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/maki/.local/bin</string>
<key>HOME</key>
<string>/Users/maki</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/maki/Library/Logs/mlx-serve.out.log</string>
<key>StandardErrorPath</key>
<string>/Users/maki/Library/Logs/mlx-serve.err.log</string>
</dict>
</plist>
EOF
LaunchDaemon は root所有・644 でないと読み込まれない。
sudo chown root:wheel /Library/LaunchDaemons/am.ik.mlx-serve.plist
sudo chmod 644 /Library/LaunchDaemons/am.ik.mlx-serve.plist
サービス読み込み
mkdir -p /Users/maki/Library/Logs
sudo launchctl bootstrap system /Library/LaunchDaemons/am.ik.mlx-serve.plist
プロセス確認
$ sudo launchctl list | grep am.ik.mlx-serve
3577 0 am.ik.mlx-serve
ログ確認
$ tail -f /Users/maki/Library/Logs/mlx-serve.out.log /Users/maki/Library/Logs/mlx-serve.err.log
==> /Users/maki/Library/Logs/mlx-serve.out.log <==
==> /Users/maki/Library/Logs/mlx-serve.err.log <==
POST /v1/embeddings
POST /v1/messages (Anthropic)
POST /v1/responses (OpenAI Responses)
POST /v1/responses/compact
GET /v1/responses/{id}
DEL /v1/responses/{id}
POST /tokenize
POST /detokenize
RSS: 19M Mem: 17% CPU: 2% GPU: 0%
APIの動作確認
curl -s http://localhost:11234/v1/models
次のレスポンスが返る
{"object":"list","data":[{"id":"ddalcu/Qwen3.8-27B-MLX-Serve-4bit","object":"model","created":0,"owned_by":"mlx-serve","loaded":false,"state":"unloaded","bytes_resident":0,"bytes_on_disk":18196477654,"context_length":262144,"max_model_len":262144,"capabilities":["chat","tool_use","streaming","json_schema","vision"],"input_modalities":["text","image","video"],"meta":{"architecture":"qwen3_5","engine":"mlx","vocab_size":248320,"hidden_size":5120,"num_layers":64,"quantization":"4-bit","context_length":262144,"model_max_tokens":262144,"is_moe":false,"bytes_on_disk":18196477654}},{"id":"ddalcu/Qwen3.8-27B-MLX-Serve-8bit","object":"model","created":0,"owned_by":"mlx-serve","loaded":false,"state":"unloaded","bytes_resident":0,"bytes_on_disk":31193577575,"context_length":262144,"max_model_len":262144,"capabilities":["chat","tool_use","streaming","json_schema","vision"],"input_modalities":["text","image","video"],"meta":{"architecture":"qwen3_5","engine":"mlx","vocab_size":248320,"hidden_size":5120,"num_layers":64,"quantization":"8-bit","context_length":262144,"model_max_tokens":262144,"is_moe":false,"bytes_on_disk":31193577575}}]}
curl -s http://localhost:11234/v1/chat/completions \
--json '{
"model": "ddalcu/Qwen3.8-27B-MLX-Serve-4bit",
"messages": [
{"role": "user", "content": "Give me a joke."}
]
}' | jq .
次のレスポンスが返る
{
"id": "chatcmpl-1789110431231",
"object": "chat.completion",
"created": 1789110431,
"model": "ddalcu/Qwen3.8-27B-MLX-Serve-4bit",
"system_fingerprint": "mlx-serve",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Why did the scarecrow win an award?\n\nBecause he was outstanding in his field! 🌾"
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 17,
"completion_tokens": 21,
"total_tokens": 38,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"timings": {
"prompt_n": 17,
"cached_n": 0,
"prompt_ms": 383.554,
"prompt_per_second": 44.322,
"predicted_n": 21,
"predicted_ms": 1015.925,
"predicted_per_second": 20.671,
"tokenize_ms": 0.485
}
}
運用系コマンドメモ
| 操作 | コマンド |
|---|---|
| 再起動 | sudo launchctl kickstart -k system/am.ik.mlx-serve |
| 停止・アンロード | sudo launchctl bootout system/am.ik.mlx-serve |
| 再度読み込み | sudo launchctl bootstrap system /Library/LaunchDaemons/am.ik.mlx-serve.plist |
| 状態確認 | sudo launchctl list | grep am.ik.mlx-serve |
| plist編集後の反映 | 一度 bootout → bootstrap し直す |
スリープ防止
sudo pmset -a sleep 0 disksleep 0