This tutorial walks you from a fresh clone to sending your first chat completion request.
- Apple Silicon Mac (M1/M2/M3/M4) — required for MLX
- Rust toolchain (1.75+ recommended)
- Python 3.10+
uvpackage manager- Model weights (e.g., a
mlx-communityquantized model)
git clone <repo-url> mlx-server-runtime
cd mlx-server-runtime
# Build the Rust gateway and protocol crates
cargo build --workspace
# Set up the Python environment
cd python
uv sync
cd ..Set your model identifier. Small models (3B–8B parameters, 4-bit) are best for first runs:
export MLX_RUNTIME_MODEL="mlx-community/Qwen2.5-7B-Instruct-4bit"The model will be downloaded from Hugging Face on first load if not cached locally.
Edit config/runtime.toml (or rely on defaults). The key fields:
| Field | Default | Purpose |
|---|---|---|
worker.model |
mlx-community/Qwen2.5-7B-Instruct-4bit |
Hugging Face model ID |
worker.ipc_path |
/tmp/mlx-runtime.sock |
Unix domain socket path |
server.port |
8000 |
HTTP server port |
cargo run --bin gatewayThis command:
- Starts the Rust HTTP server on
127.0.0.1:8000. - Spawns the Python worker as a child process.
- Waits for the worker to load the model and signal readiness.
Watch the logs. Model loading takes 5–60 seconds depending on model size.
Probe readiness during startup:
curl http://127.0.0.1:8000/startupExpected output while loading:
{"status":"starting","phase":"loading_weights","elapsed_seconds":3}When ready:
curl http://127.0.0.1:8000/readyExpected output:
{"status":"ready","ready":true,...}The legacy health endpoint also works:
curl http://127.0.0.1:8000/health
# healthycurl -X POST http://127.0.0.1:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "mlx-community/Qwen2.5-7B-Instruct-4bit",
"messages": [
{"role": "user", "content": "What is the capital of France?"}
],
"max_tokens": 32
}'Expected response:
{
"id": "chatcmpl-req-1",
"object": "chat.completion",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "The capital of France is Paris."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 19,
"completion_tokens": 8,
"total_tokens": 27
}
}curl -N -X POST http://127.0.0.1:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "mlx-community/Qwen2.5-7B-Instruct-4bit",
"messages": [
{"role": "user", "content": "Count to 5."}
],
"max_tokens": 32,
"stream": true
}'You will receive Server-Sent Events (SSE) with token deltas:
data: {"id":"chatcmpl-req-2","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":"1"},"finish_reason":null}]}
data: {"id":"chatcmpl-req-2","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":" 2"},"finish_reason":null}]}
...
data: {"id":"chatcmpl-req-2","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
curl http://127.0.0.1:8000/metricsThis returns Prometheus-formatted text with counters, gauges, and histograms.
curl http://127.0.0.1:8000/models
curl http://127.0.0.1:8000/models/mlx-community%2FQwen2.5-7B-Instruct-4bit/status
curl http://127.0.0.1:8000/models/mlx-community%2FQwen2.5-7B-Instruct-4bit/readyPress Ctrl+C on the gateway process. Rust will terminate the Python worker and clean up the Unix domain socket.
| Symptom | Likely Cause | Fix |
|---|---|---|
/ready returns 503 |
Model still loading | Wait. Check /startup for phase. |
/health returns unhealthy |
Worker not ready | Check logs for model load errors. |
| Connection refused | Gateway not running | Verify cargo run is executing. |
| Worker crashes after spawn | Missing Python deps | Run cd python && uv sync. |
model does not match error |
HTTP model field mismatch |
Use the exact model name from config. |