Ollama — Run LLMs Locally on Mac, Linux and Windows 2026

Sanjeev SharmaSanjeev Sharma
7 min read

Advertisement

Introduction

Why This Matters

Running LLMs locally with Ollama gives you complete privacy, zero API costs, and offline capability — three properties cloud APIs cannot provide. Every prompt stays on your machine. There is no per-token billing. No network dependency means the model responds even when your internet is down.

Ollama wraps the llama.cpp inference engine behind a clean REST API and a dead-simple CLI. It handles model downloading, quantization selection, and GPU acceleration automatically. You run one command and have a production-quality model responding in seconds. For developers building privacy-sensitive applications, experimenting with multiple models, or working in air-gapped environments, Ollama is the definitive local inference solution in 2026.

Installation

macOS

# Option 1: Homebrew
brew install ollama
 
# Option 2: Direct download
curl -fsSL https://ollama.com/install.sh | sh
 
# Start the Ollama server
ollama serve

Ollama automatically uses Apple Silicon GPU (Metal) on M1/M2/M3/M4 Macs, delivering 5-10x faster inference than CPU.

Linux

curl -fsSL https://ollama.com/install.sh | sh
 
# Enable as a system service
sudo systemctl enable ollama
sudo systemctl start ollama

NVIDIA GPU acceleration is detected automatically via CUDA. AMD ROCm is also supported on Linux.

Windows

Download the installer from ollama.com. The service starts automatically after installation. GPU support requires an NVIDIA card with up-to-date drivers.

Downloading and Managing Models

# Pull a model (downloads and caches locally)
ollama pull llama3          # Meta Llama 3 8B — best general-purpose
ollama pull mistral         # Mistral 7B — fast, high quality
ollama pull phi3            # Microsoft Phi-3 Mini — ultra-compact
ollama pull gemma2          # Google Gemma 2 9B — strong reasoning
ollama pull qwen2           # Alibaba Qwen 2 — multilingual
ollama pull codellama       # Meta Code Llama — code generation
ollama pull deepseek-coder  # DeepSeek Coder — code specialist
 
# List locally cached models
ollama list
 
# Show model metadata and architecture
ollama show llama3
 
# Remove a model to reclaim disk space
ollama rm mistral
 
# Pull a specific quantization variant
ollama pull llama3:8b-instruct-q4_K_M   # 4-bit quantized, ~4.7GB
ollama pull llama3:70b-instruct-q4_K_M  # 70B quantized, ~40GB

Running Models from the CLI

# Interactive REPL
ollama run llama3
 
# Single-shot prompt from command line
ollama run mistral "Explain the difference between TCP and UDP in two sentences."
 
# Pipe text in from stdin
echo "Summarize this: $(cat article.txt)" | ollama run llama3
 
# Run with custom parameters
ollama run mistral --verbose "Write a haiku about Python programming"

Python Integration via HTTP API

Ollama exposes a REST API on localhost:11434 by default. You can call it directly with requests or use the official Python library.

pip install ollama
import ollama
 
# Simple generation
response = ollama.generate(
    model="llama3",
    prompt="What is the difference between a list and a tuple in Python?",
)
print(response["response"])
# Chat with message history
messages = [
    {"role": "system", "content": "You are a senior Python engineer."},
    {"role": "user", "content": "How do I implement a context manager?"},
]
 
response = ollama.chat(model="llama3", messages=messages)
print(response["message"]["content"])

Streaming Responses

import ollama
 
def stream_response(prompt: str, model: str = "llama3") -> str:
    """Stream tokens to stdout and return full response."""
    full_response = ""
    stream = ollama.generate(model=model, prompt=prompt, stream=True)
    for chunk in stream:
        token = chunk["response"]
        print(token, end="", flush=True)
        full_response += token
    print()
    return full_response
 
stream_response("Write a Python decorator that logs function execution time.")

Building a Multi-turn Chat Application

import ollama
 
class LocalChatBot:
    def __init__(self, model: str = "llama3", system_prompt: str = "You are a helpful assistant."):
        self.model = model
        self.messages = [{"role": "system", "content": system_prompt}]
 
    def chat(self, user_input: str) -> str:
        self.messages.append({"role": "user", "content": user_input})
 
        response = ollama.chat(
            model=self.model,
            messages=self.messages,
            options={"temperature": 0.7, "num_predict": 512},
        )
 
        assistant_content = response["message"]["content"]
        self.messages.append({"role": "assistant", "content": assistant_content})
        return assistant_content
 
    def reset(self):
        self.messages = [self.messages[0]]  # Keep system prompt
 
# Usage
bot = LocalChatBot(model="llama3", system_prompt="You are a Python tutor.")
print(bot.chat("What is a generator?"))
print(bot.chat("Show me a practical example."))
print(bot.chat("How does it compare to returning a list?"))

LangChain Integration

pip install langchain langchain-community langchain-ollama
from langchain_ollama import OllamaLLM, OllamaEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
 
# LLM
llm = OllamaLLM(model="llama3", base_url="http://localhost:11434")
 
# Simple chain
prompt = ChatPromptTemplate.from_template(
    "Explain {concept} to a {audience} in 3 bullet points."
)
 
chain = prompt | llm | StrOutputParser()
 
result = chain.invoke({"concept": "neural networks", "audience": "high school student"})
print(result)
# Local RAG with Ollama embeddings
from langchain_community.vectorstores import Chroma
from langchain_core.documents import Document
 
embeddings = OllamaEmbeddings(model="nomic-embed-text")
 
docs = [
    Document(page_content="Ollama runs LLMs locally on consumer hardware."),
    Document(page_content="LangChain provides composable AI application primitives."),
    Document(page_content="RAG combines retrieval with generation for grounded answers."),
]
 
vectorstore = Chroma.from_documents(docs, embedding=embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})
 
from langchain.chains import RetrievalQA
qa = RetrievalQA.from_chain_type(llm=llm, retriever=retriever)
answer = qa.run("What is Ollama used for?")
print(answer)

Creating Custom Models with Modelfiles

A Modelfile lets you bake in a system prompt, set parameters, and create a named model ready for immediate use.

cat > Modelfile << 'EOF'
FROM llama3
 
SYSTEM You are an expert Python code reviewer. Review code for correctness, performance, and style. Be concise and specific.
 
PARAMETER temperature 0.3
PARAMETER num_predict 1024
PARAMETER repeat_penalty 1.1
EOF
 
ollama create py-reviewer -f Modelfile
ollama run py-reviewer "Review this: def fib(n): return fib(n-1)+fib(n-2) if n>1 else n"

Performance Comparison

ModelSize (disk)RAM neededTokens/sec (M2 Pro)Quality
phi3:mini2.3 GB3 GB90Good
mistral:7b4.1 GB5 GB55Very good
llama3:8b4.7 GB6 GB50Excellent
gemma2:9b5.4 GB7 GB42Excellent
llama3:70b (q4)40 GB48 GB8Best

Common Mistakes

  • Running large models without enough RAM — models spill to swap disk, making them 50x slower; check ollama show for memory requirements
  • Not pinning a quantization level — the default pull picks a quantization Ollama deems suitable; for reproducible benchmarks, always pull the specific quantization tag
  • Ignoring GPU usage — run nvidia-smi or Activity Monitor (Mac) to confirm the GPU is being used; if not, reinstall drivers or check CUDA version compatibility
  • Creating new Modelfiles instead of using --system flag — for one-off experiments use ollama run llama3 --system "You are..." rather than creating a permanent model
  • Calling the raw HTTP API without setting stream: false — the HTTP endpoint streams JSON objects by default; parse accordingly or set "stream": false in the request body

Best Practices

  • Use ollama serve as a background service and interact via the Python library rather than spawning subprocesses
  • Prefer 4-bit quantized models (tag suffix q4_K_M) for the best quality-to-memory trade-off on consumer hardware
  • For embeddings in RAG pipelines, use nomic-embed-text — it is compact and fast and outperforms most 7B LLM embeddings on retrieval benchmarks
  • Set OLLAMA_NUM_PARALLEL=2 (environment variable) if you need to serve multiple concurrent users
  • Monitor RAM usage with ollama ps to see which models are loaded and their memory footprint

Key Takeaways

  • Ollama is a one-command local LLM runner that wraps llama.cpp with a REST API and a CLI, supporting Mac, Linux, and Windows
  • Apple Silicon (M1–M4) and NVIDIA GPUs are automatically used for acceleration — CPU fallback works but is 5-10x slower
  • The official Python library (pip install ollama) provides ollama.generate() and ollama.chat() functions that mirror the OpenAI SDK style
  • Modelfiles let you create named custom models with baked-in system prompts and generation parameters, shareable like Dockerfiles
  • LangChain's OllamaLLM and OllamaEmbeddings classes enable fully local RAG pipelines with zero cloud dependencies
  • 4-bit quantized models (e.g., llama3:8b-instruct-q4_K_M) run on 6 GB of RAM while retaining ~95% of full-precision quality
  • Ollama is best for privacy-sensitive workloads, offline environments, development experimentation, and cost-free prototyping
  • For high-concurrency production workloads, combine multiple Ollama instances behind a load balancer or switch to a cloud inference API

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro

Related reading