Local LLMs on macOS: Ollama, LM Studio, llama.cpp, MLX, and Apple Silicon
Many everyday prompts do not need a remote API. Apple Silicon can run useful language models locally because the CPU and GPU share one unified memory pool, although the model file is only part of the memory budget.
The main choice in this guide is the tool around the model. Ollama, LM Studio, llama.cpp, and MLX-LM overlap, but they expose different levels of control: a managed local service, desktop exploration, direct GGUF execution, or Apple-native Python. Choose the model on task quality first, then choose the tool for the interface and artifact you need.
TL;DR. Use Ollama for a managed local service, LM Studio for desktop model exploration and its local APIs, llama.cpp for direct control over GGUF execution, and MLX-LM for Apple-native Python experimentation. None is universally fastest. Benchmark the exact model, quantization, context, and workload with memory headroom.
Start with a memory envelope
Raw quantized weight size is only the first term:
peak memory ≈ model weights
+ KV cache
+ runtime workspace
+ multimodal components
+ application and OS memory
Context length, cache dtype, parallel requests, and model architecture change the result. A nominal four-bit 7B or 8B model can still be uncomfortable on an 8 GB Mac because the operating system cannot give the runtime all installed memory.
Use Activity Monitor or the runtime’s own metrics while testing. Leave enough headroom to avoid memory pressure and swap; a model that loads but pushes the system into sustained swap is not a good interactive fit.
Also separate local inference from offline operation. Prompts can stay on the machine while the application still reaches the network for model downloads, updates, or optional features. Download artifacts first, disconnect the network, and verify the workflow if offline operation is a requirement.
The four tools solve different workflow problems
| Tool | Primary interface | Main artifact path | Choose it when |
|---|---|---|---|
| Ollama | CLI and local HTTP API | Managed model bundles, commonly GGUF-backed | An application needs a simple managed local service |
| LM Studio | Desktop UI, CLI, SDKs, local APIs | Downloaded local models including GGUF and MLX paths | A person needs to discover, compare, inspect, and serve models visually |
| llama.cpp | CLI, C/C++ library, local server | GGUF | You need direct flags, conversion/quantization tools, or embedding control |
| MLX-LM | Python and CLI | MLX-compatible weights | You are developing Python workflows specifically for Apple Silicon |
This is a responsibility table, not a speed ranking. Several tools may use related kernels or formats, and performance changes with model support and release.
Ollama: managed local service
Ollama manages model downloads, templates, process lifecycle, and a localhost API. It is useful when application code should target a stable local service rather than own inference flags.
ollama pull <model>
ollama run <model>
curl http://localhost:11434/api/chat \
-H 'Content-Type: application/json' \
-d '{
"model": "<model>",
"messages": [{"role": "user", "content": "Explain unified memory."}],
"stream": false
}'
Inspect the model manifest and context configuration rather than assuming a short library name identifies an immutable checkpoint. Pin or record the exact artifact for evaluations.
Ollama trades some low-level visibility for lifecycle convenience. Drop to llama.cpp or another runtime when you need to control a GGUF file, chat template, cache setting, or new backend feature directly.
LM Studio: desktop exploration and local APIs
LM Studio is useful when model discovery, load configuration, chat inspection, and side-by-side human evaluation belong in one desktop workflow. Its server supports native SDKs and compatibility endpoints.
Current Python SDK usage looks like this:
import lmstudio as lms
with lms.Client() as client:
model = client.llm.model("<downloaded-model-key>")
response = model.respond("Write one sentence about local inference.")
print(response)
Start the local API from the Developer tab or with:
lms server start
LM Studio can serve on localhost or a local network and supports API tokens. Keep it on loopback unless remote access is deliberate; network binding turns a private desktop model into a service that needs authentication, firewall policy, and safe tool configuration.
llama.cpp: direct GGUF execution
llama.cpp is the reference path when the artifact is GGUF and you want to see the runtime boundary directly. It supports Apple Metal along with CPU and other hardware backends.
brew install llama.cpp
# Download through the Hugging Face integration and select a quantization.
llama-cli -hf <publisher>/<gguf-repository>:Q4_K_M
# Or start an OpenAI-compatible local server.
llama-server -hf <publisher>/<gguf-repository>:Q4_K_M
The repository’s current -hf path can download a matching multimodal projector when available. Model support, templates, and CLI flags change quickly, so pin a known build and keep the launch command with the evaluation record.
Choose llama.cpp when direct control is the goal, not because “lower-level” automatically means faster. A managed tool may choose good defaults; direct flags can also make performance worse.
MLX-LM: Apple-native Python work
MLX-LM builds on Apple’s MLX array framework. It supports generation, chat, conversion, quantization, and parameter-efficient fine-tuning for compatible models.
uv add mlx-lm
uv run mlx_lm.generate \
--model mlx-community/<compatible-model> \
--prompt "Explain Metal acceleration in one paragraph."
Python exposes the model and tokenizer directly:
from mlx_lm import generate, load
model, tokenizer = load("mlx-community/<compatible-model>")
messages = [{"role": "user", "content": "Give one local-LLM benchmark rule."}]
prompt = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
)
print(generate(model, tokenizer, prompt=prompt, max_tokens=80))
MLX-LM’s HTTP server is documented as a development server with basic security checks, not a production service. Use it for local experimentation or place a reviewed application boundary in front of it.
A fair benchmark takes less time than a bad download
Test the same checkpoint family and comparable quantization where formats allow. Use a small prompt set containing:
- one short interactive prompt
- one long prompt near the intended context
- structured output or tool calls if the application needs them
- a representative generation length
- one repeated request to distinguish cold load from warm inference
Record:
| Metric | Why it matters |
|---|---|
| Task result | A fast wrong model is not useful |
| Time to first token | Interactive responsiveness |
| Output tokens per second | Generation throughput |
| Peak memory and pressure | Whether the machine remains usable |
| Cold load time | Desktop and on-demand experience |
| Energy and thermal behavior | Sustained laptop use |
| API/schema compatibility | Whether the tool fits the application |
Do not compare one tool’s 4-bit model with another tool’s full-precision model and attribute the difference to the runtime.
Security and privacy checklist
- Bind APIs to loopback unless network access is required.
- Add authentication before LAN exposure.
- Treat model files as third-party artifacts; record source, revision, license, and hash.
- Avoid executing unreviewed custom model code.
- Confirm whether optional document, tool, update, or analytics features make network calls.
- Do not assume local generation makes retrieved documents, logs, or tool side effects safe.
Conclusion
The useful choice is not “best Mac LLM app.” It is the smallest stack that exposes the control surface your workflow needs. Ollama manages a service, LM Studio manages a desktop exploration loop, llama.cpp exposes GGUF execution, and MLX-LM exposes Apple-native Python.
Give each candidate the same task, context, and memory envelope. The result will be more reliable than hardware folklore or a static star-rating table—and easy to revisit when the tools change.