Ollama#
Ollama runs on three hosts. work-laptop-m4 (M4 Pro, Metal) is the primary
inference engine for the document pipeline; mokou (GTX 1080, Pascal) is the
always-on fallback; ereshkigal runs a loopback ollama loading only
nomic-embed-text (RAG embeddings) — see the AI overview for the
full topology.
Ollama on work-laptop-m4 (darwin, primary)#
The M4 runs via tsunaminoai.ollama (modules/darwin/ollama — a launchd
system daemon; neither nix-darwin nor a user agent fit, see the module header).
Metal needs no acceleration overrides. It binds its Tailscale IP only
(100.74.80.8:11434) — never 0.0.0.0 on a work machine that roams.
Model budget on 24 GB unified memory (GPU-usable ≈ 16 GB by default):
| Set | Models | Why |
|---|---|---|
| Pipeline parity | qwen2.5:7b-instruct-q4_K_M, minicpm-v, nomic-embed-text |
Must mirror mokou so proxy failover is lossless |
| Batch quality (M4-only) | qwen2.5:14b-instruct-q4_K_M (~9 GB), richardyoung/olmocr2:7b-q8 (~9.5 GB @ 16k ctx) |
Used by the re-processing runbook, pointed directly at the M4 (never the proxy — neither fits mokou’s 8 GB). olmocr2 is the re-OCR campaign VLM (curate.visionModel); on-ingest OCR stays on the parity model minicpm-v. |
A 32b-q4 (~20 GB) does not fit alongside anything else on 24 GB — skip it on this machine. Laptop asleep/away = requests fail over to mokou at 7b quality; that is by design.
Ollama on mokou (fallback)#
Hardware#
| GPU | NVIDIA GeForce GTX 1080 |
| Compute capability | SM 6.1 (Pascal) |
| VRAM | 8 GB GDDR5X |
| CPU | Intel i7-4790K |
The CUDA SM 6.1 problem#
nixpkgs builds Ollama with CUDA support targeting SM 7.5+ (Turing and newer)
by default. Pascal (GTX 10-series, SM 6.1) is excluded, so a stock
services.ollama.acceleration = "cuda" silently falls back to CPU inference —
you get correct output but at 1/10th the speed.
The fix is to override cudaArches at the package level:
services.ollama = {
enable = true;
acceleration = "cuda";
package = pkgs.ollama.override {
acceleration = "cuda";
cudaArches = ["61"]; # SM 6.1 = GTX 1080 / Pascal
};
...
};
This rebuilds Ollama with a PTX/SASS target for SM 6.1. Build time is significant (~15–30 min on first switch); after that inference is GPU-accelerated and sub-second for 3b models.
Warning
If you see ollama run qwen2.5:3b responding in seconds but the process shows
100% CPU in htop, the CUDA override likely didn’t take. Check with
nvidia-smi during inference — GPU utilisation should be nonzero.
NixOS configuration#
# hosts/x86_64-nixos/mokou/default.nix
services.ollama = {
enable = true;
acceleration = "cuda";
package = pkgs.ollama.override {
acceleration = "cuda";
cudaArches = ["61"];
};
host = "0.0.0.0"; # listen on all interfaces, not just localhost
port = 11434;
loadModels = [
"minicpm-v" # Pipeline VLM: vision OCR (paperless-gpt + paperless-reocr)
"qwen2.5:3b" # Text model for tagging / titling / agents
"qwen2.5-coder:7b" # Code model for offline flake maintenance (llm CLI)
"qwen2.5-coder:3b" # Smaller coder fallback under VRAM pressure
"nomic-embed-text" # Embeddings for RAG / semantic search
];
home = "/data/ollama";
environmentVariables = {
OLLAMA_MAX_QUEUE = "4";
};
};
Models live on an external LUKS-encrypted ext4 disk (cryptdata) mounted at
/data, so mokou can’t use the module’s default DynamicUser. Instead it
defines a static users.users.ollama system user (with home = "/data/ollama")
and forces the service off DynamicUser:
# hosts/x86_64-nixos/mokou/default.nix
systemd.services.ollama.serviceConfig = {
DynamicUser = lib.mkForce false;
PrivateUsers = lib.mkForce false;
User = lib.mkForce "ollama";
Group = lib.mkForce "ollama";
};
(ereshkigal’s local ollama keeps the default home = "/var/lib/ollama".)
host = "0.0.0.0" is required so other hosts on Tailscale can reach the endpoint.
The firewall opens port 11434 (networking.firewall.allowedTCPPorts = [11434]),
which exposes it on the LAN and Tailscale — keep it off the internet.
Models#
| Model | Size | Use |
|---|---|---|
minicpm-v |
~5.5 GB VRAM | Vision OCR — paperless-gpt image processing + paperless-reocr |
qwen2.5:3b |
~2 GB VRAM | Text — tagging, titling, agents |
qwen2.5-coder:7b |
~4.7 GB VRAM | Code — offline flake maintenance via the llm CLI |
qwen2.5-coder:3b |
~2 GB VRAM | Smaller coder fallback under VRAM pressure |
nomic-embed-text |
~274 MB VRAM | Embeddings — RAG, semantic routing |
loadModels pre-pulls these on activation so the first request doesn’t stall
waiting for a download — and so the coder models are present offline, which is
the whole point of the llm CLI outage workflow. The two qwen2.5-coder
models fit the 8 GB card (Ollama unloads idle models, so the vision and coder models
swap rather than co-resident).
Compute capability reference#
| Architecture | SM version | Example GPUs |
|---|---|---|
| Pascal | 6.0, 6.1 | GTX 10-series, Titan X (Pascal) |
| Volta | 7.0 | Titan V, Tesla V100 |
| Turing | 7.5 | GTX 16/RTX 20-series ← nixpkgs default floor |
| Ampere | 8.0, 8.6 | RTX 30-series |
| Ada Lovelace | 8.9 | RTX 40-series |
If you have a Turing or newer GPU, the nixpkgs default works without the override.