On an RTX 3060 12GB, Ollama and llama.cpp deliver essentially the same peak tokens-per-second because Ollama is a wrapper around llama.cpp. Pick Ollama if you want a one-command setup, model registry, and REST API out of the box. Drop to raw llama.cpp when you need per-layer offload control, exotic quants that Ollama hasn't packaged yet, or the absolute latest architecture support.
Who this article is for
You bought a 12GB card for local LLMs — most often an MSI RTX 3060 Ventus 2X, a ZOTAC Twin Edge, or a GIGABYTE Gaming OC. You're staring at two options — Ollama or llama.cpp — and every forum thread contradicts the last one. You want a straight answer that's grounded in the hardware you actually own.
That's what this piece delivers. We assume you already know what a GGUF file is, what quantization does, and roughly what n_gpu_layers means. Everything below is scoped to a single-user desktop rig on a 12GB card, typically paired with an AMD Ryzen 7 5800X or similar-tier CPU. Multi-GPU, server-scale throughput, and workstation cards are a different conversation.
We do not run first-party benchmarks in this piece. Numbers cited are synthesized from public reports on the llama.cpp discussions, the Ollama repository, and independent community write-ups, filtered against the RTX 3060's TechPowerUp specification sheet — 12GB GDDR6, 360 GB/s of bandwidth, 12.7 TFLOPs FP32 — because that ceiling matters more than which runner you pick.
Key takeaways
- Peak throughput is roughly the same because Ollama wraps llama.cpp; the runner is not the ceiling — your VRAM, quant, and layer count are.
- Ollama is faster to install and update, with a built-in registry and OpenAI-compatible API. That saves hours of first-day friction.
- llama.cpp exposes every knob — precise
n_gpu_layers, custom sampler chains, arbitrary quants, and freshly-supported architectures — that Ollama hides or lags on by days to weeks. - On the 12GB RTX 3060 the practical outcome is: default to Ollama, keep raw llama.cpp installed for the specific cases where you need it.
- Both consume the same GGUF files, so switching between them is cheap. Do not treat this as a permanent commitment.
What's actually different under the hood?
Ollama is not a competitor to llama.cpp — it is a distribution of it. The current Ollama binary bundles a vendored copy of llama.cpp as its inference engine and adds a model registry, a background daemon, a REST API, and a CLI opinionated around "pull, run, done."
Practical implication: peak inference speed on the same GGUF file, same quant, and same layer offload should be within a few percent between the two. What differs is the surface area you touch and the defaults you inherit.
- Ollama defaults trend toward "just work" — auto-detect layer count, sensible context length, temperatures that produce readable output.
- llama.cpp defaults trend toward "reproducible" — explicit flags, minimum context, no sampler magic.
Neither is wrong. They serve different mental models. The gap widens if you need something Ollama's model registry hasn't published yet, in which case llama.cpp lets you point at any GGUF file on disk.
How do they compare on tokens per second on the RTX 3060 12GB?
Here's the shape of the numbers we've seen most often reported on the RTX 3060 12GB, synthesized from community threads on llama.cpp and Ollama.
| Model | Quant | Runner | Layers offloaded | Generation tok/s |
|---|---|---|---|---|
| Llama 3.1 8B | q4_K_M | Ollama | all | 45–60 |
| Llama 3.1 8B | q4_K_M | llama.cpp | all | 46–62 |
| Mistral 7B | q5_K_M | Ollama | all | 40–52 |
| Mistral 7B | q5_K_M | llama.cpp | all | 40–54 |
| Qwen 2.5 14B | q4_K_M | Ollama | ~34/40 | 15–22 |
| Qwen 2.5 14B | q4_K_M | llama.cpp | ~35/40 | 16–24 |
| Llama 3.1 8B | q8_0 | Ollama | all | 24–32 |
| Llama 3.1 8B | q8_0 | llama.cpp | all | 25–33 |
Two things stand out. First, the gap is well inside measurement noise on every row — this is a settings and driver difference, not an engine one. Second, the layer count is where honest speedups live. Ollama's auto-detection sometimes leaves 1–2 layers on the CPU when llama.cpp with a manually-tuned --n-gpu-layers fills the card. That's your entire measured delta.
If you see wildly different numbers between the two on your rig, look at three things before anything else: n_gpu_layers, context length, and whether flash-attention is enabled. Runner choice is almost never the answer.
Which is easier to set up and update on Windows and Linux?
Ollama, comfortably.
On Windows, Ollama ships a signed installer; double-click and you have a working service on port 11434 plus a CLI. Pulling a model is ollama pull llama3.1:8b; running it is ollama run llama3.1:8b. Updates are a one-click installer refresh.
On Linux, Ollama's install script writes a systemd unit and drops the binary in /usr/local/bin. Updates are a curl … | sh re-run.
llama.cpp expects a compile. On Windows that usually means cmake with CUDA toolkit and a Visual Studio build environment; on Linux, cmake plus the CUDA developer tools. Both are well-documented, but the first-time experience easily eats an evening. Pre-built binaries exist and work for many people, though the CUDA-enabled ones lag the CPU builds by a few days.
The tradeoff is straightforward. If you value time-to-first-token, Ollama saves hours. If you want to control every flag and stay on the newest llama.cpp commit yourself, the compile is worth it.
How does GPU offload (n-gpu-layers) differ between them?
llama.cpp exposes -ngl or --n-gpu-layers as a plain integer that you set on the command line. Ollama exposes an environment variable (OLLAMA_NUM_GPU) or a per-request parameter and applies its own auto-detection when you don't set anything.
For a 12GB RTX 3060, the practical rule is the same regardless of runner: pick the highest layer count that stays under 95% VRAM utilization at your target context length. On an 8B model at q4_K_M with 8k context, that's usually "all of them." On a 14B model at q4_K_M with 8k context, that's usually 30–35 of 40 layers.
The place Ollama trips is when the auto-detection reads VRAM headroom during a moment your desktop compositor is spiking. It picks a conservative number and never revisits. If you notice under-utilization in nvidia-smi, force the count with OLLAMA_NUM_GPU or the per-request num_gpu parameter — do not wait for the auto-detect to fix itself.
Feature delta table
| Feature | Ollama | llama.cpp |
|---|---|---|
| Model registry / auto-pull | Yes | No (bring your own GGUF) |
| One-click installer (Win/Linux) | Yes | No (compile or third-party build) |
| OpenAI-compatible API | Yes | Basic HTTP server, less standard |
Fine-grained n-gpu-layers control | Env var / per-request | Command-line flag |
| Custom sampler chains | Limited | Full |
| Latest architecture support | Days–weeks lag | Same day |
| Exotic quants (q2_K_S, IQ series) | Registry-dependent | All supported |
| Multi-GPU | Basic | More knobs, more work |
| Windows CUDA build support | Bundled | Compile or curated binaries |
Nothing here is one-sided. Ollama's model registry saves hours if you use the top-20 models; it doesn't help if the model you need lives on Hugging Face and hasn't been packaged. llama.cpp's architecture-day-one support matters if you're chasing the frontier; it doesn't matter if you're happy on stable Llama 3.1.
Prefill vs generation behavior and context-length handling
Both runners share the same underlying prefill and generation kernels, so the shape of the tradeoff is identical: prefill is memory-bandwidth-bound (fast on the 3060), generation is compute-and-cache-bound (much slower), and both degrade sharply once the KV-cache spills into system RAM.
Where they differ is in how they handle context configuration. Ollama's default context length is often smaller than what your model supports. If you plan to feed long documents, override it. Set num_ctx in the Modelfile or per-request; do not assume the model's declared max is what the runner is actually using.
llama.cpp requires you to set context length explicitly via --ctx-size. That's more work up front but eliminates the surprise. Either way, remember that KV-cache VRAM grows with context — a 32k context on a 14B model can cost 6+ GB of VRAM on top of weights.
When should you drop to raw llama.cpp instead of Ollama?
Four situations, roughly in order of frequency:
- New architecture release. A model type just landed and Ollama's registry hasn't published a quant yet. llama.cpp lets you convert or run whatever GGUF the community produced today.
- Exotic quant. You want IQ3_XS, IQ2_M, or a fresh k-quant variant that Ollama hasn't exposed. llama.cpp reads whatever quant is in the file.
- Custom sampler. You need mirostat, TFS, min-p, or a chain that Ollama's API doesn't expose. llama.cpp gives you the full sampler stack.
- Reproducible benchmarks. You're publishing tokens-per-second numbers and want zero abstractions between you and the engine. Compile llama.cpp at a known commit, run with fixed flags.
If none of these apply, Ollama is the correct default.
Common pitfalls on the 12GB RTX 3060
A short list of the mistakes we see most often when someone switches runners hoping for a speed-up:
- Blaming the runner for VRAM spill. Same model, same quant, same context = same speed. If Ollama is slower, it's your layer count or context length.
- Running two runners at once. Both grab VRAM. Kill the daemon of the one you're not using.
- Chasing fp16 on a 12GB card. You will not fit a modern useful model at fp16 on this GPU. Stop tuning for it.
- Forgetting to update. Both projects ship weekly. A three-month-old install can be 15–25% slower than current on the same model.
- Ignoring the CPU. The Ryzen 7 5800X with DDR4-3600 is a strong pairing. An older Ryzen or slow DDR4 will drag partial-offload runs 20% or more.
A worked example: switching a single model between the two
Say you're running Llama 3.1 8B at q4_K_M and you want to move from Ollama to raw llama.cpp for a benchmark run. Locate Ollama's cached GGUF at ~/.ollama/models/blobs/, note the SHA-prefixed filename, and either symlink or copy it to a working directory as llama-3.1-8b-q4_k_m.gguf. From there ./llama-server -m ./llama-3.1-8b-q4_k_m.gguf -ngl 33 --ctx-size 8192 --port 8080 gives you the same model with explicit flags. Point your client at 8080 and rerun. Any speed delta is now a flag issue you can diagnose, not a runner mystery.
When NOT to use either
Skip both if your workload is a few queries a day at short prompts and you don't need privacy or offline. A hosted API is cheaper, faster, and higher quality per dollar than a rig you built for the purpose.
Skip the 3060 tier if you already know you need a mid-size model at long context. Neither runner can conjure VRAM. Save up for a 16GB or 24GB card; both runners run rings around themselves on more VRAM.
Bottom line: the right runner for a 12GB RTX 3060 rig
Install Ollama first. It handles 90% of what you need in fewer keystrokes. Keep raw llama.cpp built and ready for the 10% cases — new architectures, exotic quants, custom samplers, benchmarks.
You don't have to pick one forever. Both consume GGUF files, so the same model file works in both. Switch as needed and stop stressing about it. The card is the bottleneck; whichever runner gets your layer count right at your target context length wins by a few percent that you will not notice in a real workload on a 12GB RTX 3060.
Related guides
- GLM-5.2 review — is it the best open-weights model?
- Match your GPU to the model: a per-LLM hardware guide
- MSI RTX 3060 Ventus 2X 12G — SpecPicks pick
- ZOTAC RTX 3060 Twin Edge — SpecPicks pick
Citations and sources
- llama.cpp GitHub repository — engine source, community throughput numbers, quant format specs
- Ollama GitHub repository — model registry, install scripts, layer-offload defaults
- TechPowerUp — GeForce RTX 3060 specification sheet — GDDR6 bandwidth and FP32 compute reference
