A CLI tool to manage vLLM Metal on Apple Silicon Macs. Start, stop, and expose a local LLM server with one command — including an ngrok tunnel for use with Cursor IDE and other OpenAI-compatible clients that block localhost.
Running local LLMs on Apple Silicon via vLLM Metal works, but managing the server, checking status, and exposing it to tools like Cursor involves juggling multiple processes. vllm-ctl wraps all of that into a single CLI:
- One command to start — launches vLLM Metal + ngrok tunnel
- One command to stop — kills both cleanly
- Prints Cursor-ready config — copy-paste into Cursor settings
- Status, logs, test — all built in
- macOS on Apple Silicon (M1/M2/M3/M4)
- Homebrew
- ngrok account (free tier works) — for tunnel support
- Hugging Face account — for model downloads
curl -fsSL https://raw.githubusercontent.com/yafatek/vllm-ctl/main/install.sh | bashOr manually:
git clone https://github.com/yafatek/vllm-ctl.git
cd vllm-ctl
chmod +x vllm-ctl
ln -sf "$(pwd)/vllm-ctl" "$HOME/.local/bin/vllm-ctl"Then install the vLLM Metal backend:
vllm-ctl installvllm-ctl loginngrok config add-authtoken <your-token>Get your free token at dashboard.ngrok.com.
vllm-ctl startOutput:
Starting vLLM Metal server...
Model: mlx-community/Qwen3-Coder-30B-A3B-Instruct-4bit
Port: 8000
Waiting for server..............
Server ready!
Starting ngrok tunnel...
Waiting for tunnel URL....
=== vLLM Metal ===
Model: mlx-community/Qwen3-Coder-30B-A3B-Instruct-4bit
Local: http://localhost:8000/v1/
Tunnel: https://your-subdomain.ngrok-free.dev/v1/
Cursor Settings:
API Key: sk-local
Base URL: https://your-subdomain.ngrok-free.dev/v1
Model: mlx-community/Qwen3-Coder-30B-A3B-Instruct-4bit
vllm-ctl start mlx-community/Qwen2.5-Coder-14B-Instruct-4bitvllm-ctl stopvllm-ctl statusvllm-ctl urlvllm-ctl test "Write a Python function that checks if a number is prime"vllm-ctl download mlx-community/Qwen3-Coder-30B-A3B-Instruct-4bit| Command | Description |
|---|---|
vllm-ctl install |
Install vLLM Metal backend |
vllm-ctl uninstall |
Remove vLLM Metal and optionally cached models |
vllm-ctl start [model] |
Start server + ngrok tunnel |
vllm-ctl stop |
Stop server + tunnel |
vllm-ctl restart [model] |
Restart both |
vllm-ctl status |
Server state, tunnel URL, memory, cached models |
vllm-ctl url |
Print tunnel URL |
vllm-ctl logs |
Tail server logs |
vllm-ctl logs-tunnel |
Tail ngrok logs |
vllm-ctl models |
List loaded models |
vllm-ctl test [prompt] |
Send a test prompt |
vllm-ctl download <model> |
Pre-download a model |
vllm-ctl login |
Authenticate with Hugging Face |
vllm-ctl clean-cache |
Remove partially downloaded files |
vllm-ctl start- Copy the tunnel URL from the output
- In Cursor: Settings → Models → OpenAI API Key
- API Key:
sk-local(any non-empty string) - Override OpenAI Base URL:
<tunnel-url>/v1
- API Key:
- Click Add Model and enter the model name from
vllm-ctl status - Select the model in chat or
Cmd+K
Note: The ngrok URL changes on each restart. Run
vllm-ctl urlto get the current one.
| Model | VRAM | Speed | Quality |
|---|---|---|---|
mlx-community/Qwen3-Coder-30B-A3B-Instruct-4bit |
~17GB | Good | Excellent |
mlx-community/Qwen2.5-Coder-14B-Instruct-4bit |
~8GB | Fast | Very Good |
mlx-community/Qwen2.5-Coder-7B-Instruct-4bit |
~4GB | Very Fast | Good |
| Variable | Default | Description |
|---|---|---|
VLLM_PORT |
8000 |
Server port |
VLLM_NO_TUNNEL |
0 |
Set to 1 to skip ngrok tunnel |
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Cursor IDE │────▶│ ngrok │────▶│ vLLM Metal │
│ (or any │ │ tunnel │ │ server │
│ OpenAI │ │ │ │ (MLX on │
│ client) │ │ public URL │ │ Metal GPU) │
└──────────────┘ └──────────────┘ └──────────────┘
Apache 2.0