Run any model on your hardware — no API keys, no cloud, complete privacy.
Local LLMs have matured. You can now run 7B-70B models on consumer hardware with near-cloud performance. This guide covers the three main engines: Ollama (easiest), llama.cpp (most flexible), vLLM (highest throughput).
| Feature | Ollama | llama.cpp | vLLM |
|---|---|---|---|
| Ease of use | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
| Model formats | GGUF | GGUF | HF/Safetensors |
| API compatible | ✅ OpenAI | ✅ OpenAI | ✅ OpenAI |
| Multimodal | ✅ | ✅ | ✅ |
| Batching | ❌ | ❌ | ✅ PagedAttention |
| Quantization | Auto | Manual | Auto (AWQ/GPTQ) |
| GPU/CPU | Both | Both | GPU (CUDA/ROCm) |
| Best for | Devs, local chat | Embedded, edge | Production serving |
| Model | Quantization | VRAM/RAM | CPU Only |
|---|---|---|---|
| 7B | Q4_K_M | 6 GB | ✅ Slow |
| 7B | Q8_0 | 8 GB | ✅ Usable |
| 13B | Q4_K_M | 10 GB | ✅ Slow |
| 33B | Q4_K_M | 20 GB | ❌ |
| 70B | Q4_K_M | 40 GB | ❌ |
Rule of thumb: VRAM ≈ params × 0.5 (Q4) to 1.0 (Q8) in GB.
curl -fsSL https://ollama.com/install.sh | sh
brew install ollama
winget install Ollama.Ollama
ollama run mistral:7b-instruct-q4_K_M
ollama list
ollama show mistral:7b-instruct-q4_K_M
ollama serve
curl -X POST http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "mistral:7b-instruct-q4_K_M",
"messages": [{"role": "user", "content": "Hello!"}],
"stream": false
}'
python
import requests
def chat(messages, model="mistral:7b-instruct-q4_K_M"):
response = requests.post(
"http://localhost:11434/v1/chat/completions",
json={"model": model, "messages": messages, "stream": False}
)
return response.json()["choices"][0]["message"]["content"]
print(chat([{"role": "user", "content": "Write a haiku about GPUs"}]))
FROM ./my-model.Q4_K_M.gguf
TEMPLATE "{{ .System }}\n\n{{ .Prompt }}"
PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER num_ctx 8192
SYSTEM "You are a helpful coding assistant."
ollama create my-coder -f Modelfile
ollama run my-coder
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
make LLAMA_CUBLAS=1 # For NVIDIA GPU
huggingface-cli download meta-llama/Llama-3.1-8B-Instruct --local-dir ./llama-3.1-8b
python convert-hf-to-gguf.py ./llama-3.1-8b \
--outfile ./llama-3.1-8b.Q4_K_M.gguf \
--outtype q4_k_m
| Type | Size (7B) | Quality | Speed |
|---|---|---|---|
| Q2_K | ~2.8 GB | Low | Fastest |
| Q3_K_M | ~3.3 GB | Medium | Fast |
| Q4_K_M | ~3.8 GB | Good | Balanced |
| Q5_K_M | ~4.7 GB | Better | Slower |
| Q6_K | ~5.3 GB | High | Slower |
| Q8_0 | ~7.2 GB | Near fp16 | Slowest |
./llama-server -m ./model.Q4_K_M.gguf -c 8192 --port 8080
./llama-server -m ./model.Q4_K_M.gguf -c 8192 --port 8080 -ngl 99
./llama-server -m ./model.Q4_K_M.gguf -c 8192 --port 8080 -ngl 99 -fa
python
from llama_cpp import Llama
llm = Llama(
model_path="./model.Q4_K_M.gguf",
n_ctx=8192,
n_gpu_layers=-1, # -1 = all layers on GPU
verbose=False
)
response = llm.create_chat_completion(
messages=[{"role": "user", "content": "Hello!"}],
temperature=0.7,
max_tokens=512
)
print(response["choices"][0]["message"]["content"])
for chunk in llm.create_chat_completion(
messages=[{"role": "user", "content": "Write a story"}],
stream=True
):
delta = chunk["choices"][0]["delta"]
if "content" in delta:
print(delta["content"], end="", flush=True)
./llama-server -m ./llama-3.1-8b.Q4_K_M.gguf \
-md ./llama-3.1-1b.Q4_K_M.gguf \
-c 8192 --port 8080 -ngl 99
pip install vllm
pip install vllm --extra-index-url https://download.pytorch.org/whl/rocm6.0
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--tensor-parallel-size 1 \
--gpu-memory-utilization 0.9 \
--max-model-len 8192 \
--dtype auto \
--api-key your-secret-key
vllm serve TheBloke/Llama-3.1-8B-Instruct-AWQ \
--quantization awq \
--gpu-memory-utilization 0.9
vllm serve TheBloke/Llama-3.1-8B-Instruct-GPTQ \
--quantization gptq \
--gpu-memory-utilization 0.9
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--tensor-parallel-size 2 \
--gpu-memory-utilization 0.9 \
--max-model-len 4096
python
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="your-secret-key"
)
response = client.chat.completions.create(
model="meta-llama/Llama-3.1-8B-Instruct",
messages=[{"role": "user", "content": "Hello!"}],
temperature=0.7,
max_tokens=512
)
print(response.choices[0].message.content)
stream = client.chat.completions.create(
model="meta-llama/Llama-3.1-8B-Instruct",
messages=[{"role": "user", "content": "Write a poem"}],
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
Model: Llama-3.1-8B, Batch: 32, Context: 2048
vLLM (PagedAttention): ~450 tok/s
llama.cpp (batch 32): ~280 tok/s
Ollama (no batching): ~120 tok/s
| Size | Model | Best For |
|---|---|---|
| 1B | SmolLM2-1.7B-Instruct | Edge, mobile |
| 3B | Phi-3.5-mini-instruct | Fast, capable |
| 7B | Mistral-Nemo-12B-Instruct | Best 7B-class |
| 7B | Llama-3.1-8B-Instruct | Standard baseline |
| 7B | Qwen2.5-7B-Instruct | Multilingual, code |
| 13B | Nemotron-3-Ultra | Reasoning |
| Size | Model |
|---|---|
| 7B | CodeQwen1.5-7B |
| 7B | DeepSeek-Coder-V2-Lite |
| 13B | CodeLlama-13B-Instruct |
| 33B | DeepSeek-Coder-V2-33B |
| Size | Model |
|---|---|
| 7B | Nemotron-3-Ultra |
| 13B | QwQ-32B-Preview (distilled) |
| 33B | Nemotron-3-Ultra-33B |
./llama-server -m model.gguf -t $(nproc --all) -ngl 99
./llama-server -m model.gguf -b 512 -ub 512
./llama-server -m model.gguf -fa
vllm serve model --max-num-batched-tokens 8192 --max-num-seqs 256
vllm serve model --enable-chunked-prefill --max-model-len 32768
vllm serve model --enable-prefix-caching
OLLAMA_NUM_PARALLEL=4 # Concurrent requests
OLLAMA_MAX_LOADED_MODELS=2 # Keep models in VRAM
OLLAMA_FLASH_ATTENTION=1 # Enable flash attention
| Setup | Hardware | Models | Est. Cost |
|---|---|---|---|
| Ollama + RTX 3090 | $800 used | 7B-33B Q4 | $15/mo electricity |
| llama.cpp + RTX 4090 | $1600 | 7B-70B Q4 | $25/mo electricity |
| vLLM + 2×A100 80GB | $30k | 70B fp8 | $500/mo colocation |
| Cloud API (OpenRouter) | — | Any | $50-500/mo usage |
Break-even: Local GPU pays for itself in 6-18 months vs cloud APIs.
| Problem | Fix |
|---|---|
| OOM on GPU | Reduce n_gpu_layers , use smaller quantization, offload to CPU |
| Slow CPU inference | Use Q4_K_M , enable BLAS (LLAMA_BLAS=1 ), reduce context |
| Model not | Check GGUF compatibility, try llama.cpp convert script |
| API connection refused | Check port, firewall, OLLAMA_HOST=0.0.0.0 |
| Poor quality | Increase temperature, try larger model, better quantization |
START: What's your use case?
│
├─→ "Just chat / quick tests" → **Ollama**
│
├─→ "Embed in app / edge device / custom quant" → **llama.cpp**
│
├─→ "Serve many users / high throughput / production" → **vLLM**
│
└─→ "Apple Silicon Mac" → **Ollama** or **llama.cpp (Metal)**
Local LLMs are production-ready. Choose:
All three expose OpenAI-compatible APIs — swap providers without code changes.
Next: Add RAG (see previous tutorial), function calling, or fine-tune your own LoRA.
Part of the Local LLM series on Dev.to