Modelfiles: the Dockerfile nobody reads until they need one
I installed Ollama in the spring of 2025 and spent the next thirteen months treating models as read-only artifacts. Pull, run, done. I knew Modelfiles existed the way I know my car has a spare tire, abstractly, with no intention of ever checking.
That worked for exactly as long as I was the only user.
the drift that forced my hand
In late May our platform team (five engineers, mixed MacBooks and one Linux tower) had grown an informal code-review helper: a Qwen coder model plus a system prompt I'd written with review rules and severity labels. The prompt lived in a Slack pin. Everyone pasted it into their own client, and every copy drifted. One teammate's version still asked for XML output while mine had moved to markdown weeks earlier. Another ran at the default temperature of 0.8 and kept getting reviews with, let's say, artistic ambition.
Three drifted copies of one prompt is how one diff gets five opinions.
The fix is old. Put the config in a file, put the file in git, build from the file. Docker settled this argument in 2013. Ollama's version of the answer is the Modelfile, and it took me under an hour to stop ignoring it.
four keywords do almost all the work
FROM, PARAMETER, SYSTEM, TEMPLATE. Our whole reviewer is this:
FROM qwen2.5-coder:14b
PARAMETER temperature 0.2
PARAMETER num_ctx 16384
SYSTEM You review diffs for the platform team. Prioritize correctness over style. Cite file and line. Do not rewrite code.
Run ollama create revbot -f Modelfile and every client on the machine sees a model called revbot. FROM takes any pulled tag or a raw GGUF path. PARAMETER sets defaults: temperature, context length, stop sequences. SYSTEM bakes the prompt into the artifact itself, which is the part that ended our drift: the prompt became code that ships with the model, reviewed in pull requests like everything else we maintain. The file lives in our infra repo, and a new teammate runs one create command to get the exact reviewer everyone else has.
Prompt drift died that week.
the whitespace bug that ate tool calling
Then I got cocky. I wanted the reviewer to emit a structured verdict for CI, decided the chat template needed adjusting, dumped it with ollama show --template, pasted it under TEMPLATE in my Modelfile, and edited away. My editor helpfully trims trailing whitespace on save. Somewhere in that round trip, a newline in front of the tool-definitions block disappeared.
Nothing errored. The model just quietly stopped making structured tool calls and started describing them in prose: valid-looking JSON embedded mid-sentence, useless to the parser downstream. I spent a day and a half suspecting my CI glue, then the model, then the phase of the moon, before I diffed my template against the original and found one missing newline.
A chat template is training data you're editing by hand, and your editor's tidy-on-save is quietly corrupting it.
The model saw those exact token positions millions of times during training. Move the whitespace and you're speaking almost-right to something that has no concept of almost. My rule since: never override TEMPLATE unless the base model's template is actually broken, and if I must, diff it character by character before shipping it to anyone.
where the Dockerfile analogy breaks
Some honesty about the tool I just praised. FROM tags are mutable: qwen2.5-coder:14b can point at a different blob next month, so pin digests if reproducibility matters to you. There are no build args and no layer caching, so a family of related Modelfiles means copy-paste. And PARAMETER is a default, not a policy: any client can override temperature back up at request time, and some clients do exactly that without telling you. I accepted all three trade-offs, because the alternative was the Slack pin.
If you run Ollama solo, you genuinely don't need any of this: my day-to-day setup ran for over a year without a single Modelfile and lost nothing. The day a second person needs your exact model, write the file. Four keywords, one create command, and the pinned prompt in Slack can finally die.