Getting Started with Ollama: Running Large Language Models Locally with Docker
We earn commissions when you shop through the links on this page, at no additional cost to you. Learn more.
Running a large language model on your own hardware felt like science fiction three years ago. Today, with Ollama, you can have llama3.2 or mistral answering questions from your terminal in under ten minutes — no cloud account, no API key, no usage bill. I run Ollama on a mini PC with 32 GB of RAM and it handles day-to-day coding questions and document summarisation well enough that I rarely reach for a cloud API anymore.
In this tutorial I'll walk you through getting Ollama running inside Docker, pulling a few models, wiring up a GPU if you have one, and then adding Open WebUI so you get a proper chat interface instead of a bare REST endpoint. I'll keep things focused on Docker because it makes the whole stack reproducible and easy to update.
Why Docker Instead of the Native Installer?
Ollama ships a native installer for Linux, macOS, and Windows, and it works fine. I still prefer Docker for homelab use because I can version-pin the image, restart it alongside my other compose stacks, and tear it down without leaving anything behind on the host. It also makes GPU passthrough explicit and auditable — you know exactly what the container is touching.
The one real trade-off is a slightly more involved GPU setup. I'll cover both CPU-only and NVIDIA GPU configurations below.
Prerequisites
- Docker Engine 24+ and Docker Compose v2 installed on a Linux host
- At least 8 GB of RAM for small models (16 GB+ recommended for 7B models)
- Optional: an NVIDIA GPU with the
nvidia-container-toolkitinstalled - A few gigabytes of free disk space per model —
llama3.2:3bis about 2 GB,mistral:7bis around 4 GB
llama3.2:3b or qwen2.5:3b. They're surprisingly capable and will actually respond at a usable speed — typically 5–15 tokens per second on a modern x86 CPU.Step 1: Create the Docker Compose File
I keep all my Ollama config in ~/stacks/ollama/. Create that directory and drop in the following compose.yml. The CPU-only version is the simplest starting point:
mkdir -p ~/stacks/ollama
cd ~/stacks/ollama
nano compose.yml
services:
ollama:
image: ollama/ollama:latest
container_name: ollama
restart: unless-stopped
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
environment:
- OLLAMA_KEEP_ALIVE=24h
- OLLAMA_NUM_PARALLEL=2
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
restart: unless-stopped
ports:
- "3000:8080"
volumes:
- open_webui_data:/app/backend/data
environment:
- OLLAMA_BASE_URL=http://ollama:11434
depends_on:
- ollama
volumes:
ollama_data:
open_webui_data:
A few things I want to call out here. OLLAMA_KEEP_ALIVE=24h tells Ollama to keep a loaded model in memory for 24 hours instead of unloading it after a few minutes of inactivity — this is the single biggest quality-of-life improvement for interactive use. OLLAMA_NUM_PARALLEL=2 allows two simultaneous inference requests, which is useful once Open WebUI is in the picture. The models directory is stored in a named Docker volume at /root/.ollama inside the container, so your downloaded models survive container recreations.
Step 2: Add GPU Passthrough (NVIDIA)
If you have an NVIDIA card, first make sure the container toolkit is installed on the host:
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update && sudo apt install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
Then add the deploy section to the ollama service in your compose file:
ollama:
image: ollama/ollama:latest
container_name: ollama
restart: unless-stopped
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
environment:
- OLLAMA_KEEP_ALIVE=24h
- OLLAMA_NUM_PARALLEL=2
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
deploy.resources GPU passthrough syntax only works with docker compose (v2). If you're running the old docker-compose (v1) Python binary, you'll need the legacy runtime: nvidia key instead. Check your version with docker compose version.Step 3: Start the Stack and Pull a Model
Bring everything up:
docker compose up -d
docker compose logs -f ollama
Watch the logs for a moment to confirm Ollama started cleanly — you should see something like Listening on [::]:11434. Then pull your first model. I always start with llama3.2:3b to confirm things are working before committing to a larger download:
# Pull a 3B model (~2 GB download)
docker exec -it ollama ollama pull llama3.2:3b
# Try a quick inference from the command line
docker exec -it ollama ollama run llama3.2:3b "What is self-hosting?"
# Pull a more capable 7B model when you're ready
docker exec -it ollama ollama pull mistral:7b
# List everything you have downloaded
docker exec -it ollama ollama list
The first response from ollama run will take a few seconds while the model loads into memory. Subsequent requests in the same session are much faster because the model stays loaded thanks to the OLLAMA_KEEP_ALIVE variable we set earlier.
Step 4: Use the Open WebUI Interface
Open a browser and navigate to http://your-server-ip:3000. On first launch, Open WebUI will ask you to create an admin account — this is purely local, nothing leaves your machine. After logging in, click the model selector at the top of the chat window and you should see every model you pulled through Ollama listed there automatically.
Open WebUI has come a long way. I use it daily for document Q&A (it supports uploading PDFs and text files directly into the chat), quick code reviews, and drafting emails. The RAG pipeline built into recent versions is genuinely useful — you can point it at a folder of markdown notes and ask questions against the whole collection.
Step 5: Expose Ollama Securely (Optional)
By default, port 11434 only accepts connections from localhost inside Docker's bridge network. The compose file above publishes it on all interfaces (0.0.0.0:11434), which is fine on a private LAN but you should not expose this port directly to the internet — Ollama has no built-in authentication.
If you want to access your Ollama instance remotely, I recommend one of two approaches: tunnel it over Tailscale (zero config, zero exposed ports) or put it behind Caddy or Nginx Proxy Manager with HTTP basic auth or Authelia in front. I personally use Tailscale for Ollama — the latency is low enough that streaming token output still feels snappy over a Tailnet connection.
Useful Model Recommendations
Once the stack is running, here are the models I actually keep around and why:
- llama3.2:3b — My go-to for quick questions on CPU-only hardware. Fast, surprisingly coherent.
- mistral:7b — Better reasoning than the 3B Llama. Needs 8 GB VRAM or a beefy CPU to run comfortably.
- codellama:7b — Purpose-built for code. Noticeably better at Python and Bash than a general 7B model.
- nomic-embed-text — Not a chat model, but essential if you want Open WebUI's document embedding / RAG features to work properly.
- qwen2.5:7b — Impressive for its size, especially on instruction-following tasks. Worth trying if you find Mistral too slow.
# Pull the embedding model for Open WebUI RAG
docker exec -it ollama ollama pull nomic-embed-text
# Remove a model you no longer want (frees disk space immediately)
docker exec -it ollama ollama rm codellama:7b
Keeping Everything Updated
I use Watchtower to keep my images current, but Ollama image updates are worth reviewing manually because they sometimes change default behaviour. A safer pattern is a quick manual pull every few weeks:
cd ~/stacks/ollama
docker compose pull
docker compose up -d
Note that pulling a new Ollama image does not update the model weights themselves — those live in the ollama_data volume and are managed separately with ollama pull. You'll need to re-pull models that have received updates (like Llama or Mistral releases) manually.
Wrapping Up
You now have a fully self-hosted LLM stack: Ollama serving models over a local REST API, and Open WebUI giving you a polished chat interface that your whole household or team can use. The entire thing runs in two containers, persists its data in named volumes, and can be updated with a single docker compose pull && docker compose up -d.
From here, the natural next steps are adding a reverse proxy with SSL so you can reach it from anywhere on your Tailnet with a proper domain name, and exploring the Ollama REST API directly — it's OpenAI-compatible, which means most tools written for the OpenAI SDK will work against your local stack with a one-line URL change.
Discussion