Running LLMs locally has gone from a niche experiment to a legitimate production pattern. Privacy requirements, API cost at scale, and offline-first applications all push teams toward local inference. Ollama is the tool that made this practical — it wraps model management, a REST API, and OpenAI-compatible endpoints into a single binary.
Installing Ollama
macOS and Linux:
curl -fsSL https://ollama.com/install.sh | sh
Windows: Download the installer from ollama.com. It runs as a system service automatically.
ollama --version
curl http://localhost:11434 # → "Ollama is running"
Pulling and Running Models
ollama pull llama3.3:8b
ollama run llama3.3:8b
echo "Explain async/await in one paragraph" | ollama run llama3.3:8b
ollama list
Choosing the Right Model
| Model | Size | VRAM | Best for |
|---|---|---|---|
llama3.3:70b |
43GB | 48GB+ | Best quality |
llama3.3:8b |
4.7GB | 8GB | Most laptops |
qwen2.5-coder:7b |
4.7GB | 8GB | Fast coding |
gemma3:27b |
16GB | 20GB | Google's best local |
deepseek-r1:8b |
4.9GB | 8GB | Reasoning + CoT |
phi4:14b |
9.1GB | 12GB | Microsoft, reasoning |
Rule: pick the largest model your VRAM can hold with ~20% headroom.
TypeScript Integration
// lib/ollama.ts
interface OllamaMessage {
role: 'system' | 'user' | 'assistant'
content: string
}
const OLLAMA_URL = process.env.OLLAMA_URL ?? 'http://localhost:11434'
export async function chat(messages: OllamaMessage[], model = 'llama3.3:8b'): Promise<string> {
const response = await fetch(`${OLLAMA_URL}/api/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model, messages, stream: false })
})
if (!response.ok) throw new Error(`Ollama error: ${response.status}`)
const data = await response.json()
return data.message.content
}
export async function* streamChat(messages: OllamaMessage[], model = 'llama3.3:8b') {
const response = await fetch(`${OLLAMA_URL}/api/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model, messages, stream: true })
})
const reader = response.body!.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader.read()
if (done) break
const lines = decoder.decode(value).split('\n').filter(Boolean)
for (const line of lines) {
const data = JSON.parse(line)
if (data.message?.content) yield data.message.content
}
}
}
OpenAI SDK Compatibility
Ollama exposes an OpenAI-compatible endpoint at /v1. Use the openai package with zero code changes:
import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'http://localhost:11434/v1',
apiKey: 'ollama' // required by SDK, ignored by Ollama
})
const completion = await client.chat.completions.create({
model: 'llama3.3:8b',
messages: [
{ role: 'system', content: 'You are a code reviewer.' },
{ role: 'user', content: 'Review: SELECT * FROM users WHERE id = ' + userId }
]
})
console.log(completion.choices[0].message.content)
Switching between cloud APIs and local Ollama is often just changing baseURL and model.
Python Integration
import httpx
import json
from typing import Generator
def chat(messages: list[dict], model: str = "llama3.3:8b") -> str:
response = httpx.post(
"http://localhost:11434/api/chat",
json={"model": model, "messages": messages, "stream": False},
timeout=120.0
)
response.raise_for_status()
return response.json()["message"]["content"]
def stream_chat(messages: list[dict], model: str = "llama3.3:8b") -> Generator[str, None, None]:
with httpx.stream(
"POST",
"http://localhost:11434/api/chat",
json={"model": model, "messages": messages, "stream": True},
timeout=None
) as response:
for line in response.iter_lines():
if line:
data = json.loads(line)
if data.get("message", {}).get("content"):
yield data["message"]["content"]
With the OpenAI Python SDK:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
response = client.chat.completions.create(
model="qwen2.5-coder:7b",
messages=[{"role": "user", "content": "Write a Python email validator"}]
)
print(response.choices[0].message.content)
Custom Modelfiles
Lock in a system prompt and parameters for a specific use case:
# Modelfile.code-reviewer
FROM qwen2.5-coder:7b
SYSTEM """
You are a senior software engineer doing code reviews. Identify:
1. Bugs and security issues
2. SOLID principle violations
3. Performance problems
Format issues as: [SEVERITY] Description → fix
SEVERITY: CRITICAL | HIGH | MEDIUM | LOW
"""
PARAMETER temperature 0.2
PARAMETER num_ctx 8192
ollama create code-reviewer -f Modelfile.code-reviewer
ollama run code-reviewer
Streaming in a Next.js API Route
// app/api/chat/route.ts
export async function POST(request: Request) {
const { messages } = await request.json()
const ollamaResponse = await fetch('http://localhost:11434/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model: 'llama3.3:8b', messages, stream: true })
})
const stream = new ReadableStream({
async start(controller) {
const reader = ollamaResponse.body!.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader.read()
if (done) { controller.close(); break }
const lines = decoder.decode(value).split('\n').filter(Boolean)
for (const line of lines) {
const data = JSON.parse(line)
if (data.message?.content) {
controller.enqueue(
new TextEncoder().encode(`data: ${JSON.stringify({ content: data.message.content })}\n\n`)
)
}
if (data.done) { controller.close(); return }
}
}
}
})
return new Response(stream, {
headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }
})
}
Production Setup
# Ollama runs as a systemd service after install
systemctl status ollama
systemctl enable ollama
# Expose to network (only on private networks)
sudo systemctl edit ollama
# Add: Environment="OLLAMA_HOST=0.0.0.0:11434"
# Add: Environment="OLLAMA_NUM_PARALLEL=2"
Ollama auto-detects NVIDIA (CUDA), AMD (ROCm), and Apple Silicon (Metal). No configuration — it picks the fastest backend.
Ollama vs Cloud APIs
| Factor | Ollama | Cloud API |
|---|---|---|
| Cost at scale | ~$0 (electricity) | $0.01–$15/1M tokens |
| Privacy | Data stays local | Sent to provider |
| Quality ceiling | Llama 3.3 70B ≈ GPT-4o | Best-in-class |
| Offline | ✅ | ❌ |
| Scaling | Hardware limit | Unlimited |
Use Ollama for: sensitive data (PII, financial, medical), high-volume internal tools, offline/air-gapped deployment, development without burning API credits.
Use cloud APIs for: frontier model quality, unpredictable load, no GPU hardware.
The practical pattern: Ollama for dev and internal tools, cloud APIs (Claude Sonnet, GPT-4o) for customer-facing production features.
Full article at stacknotice.com/blog/ollama-complete-guide-2026
Top comments (0)