Ollama Modelfile Syntax, Line by Line
9 min read · updated August 11, 2026
A Modelfile is not a config file that Ollama reads at run time. It is a build script: ollama create executes it once, turns each instruction into a layer, and the layers are what the server loads afterwards. Knowing which instruction becomes which layer is most of what makes the syntax predictable.
The shape of the file
The grammar is one instruction per line, an uppercase-by-convention keyword followed by its arguments, with # starting a comment. Ollama’s Modelfile reference states that the file is not case sensitive and that instructions may appear in any order; the convention of putting FROM first and shouting the keywords is purely so a reader can tell instruction from argument at a glance.
There are eight instructions in the current reference: FROM (required), PARAMETER, TEMPLATE, SYSTEM, ADAPTER, LICENSE, MESSAGE and REQUIRES. Anything that spans more than one line — a system message with paragraph breaks, a Go template, a licence — is wrapped in triple quotes. That is the single most common syntax error: a multi-line SYSTEM without the triple quotes silently becomes a one-line system message plus several instructions Ollama does not recognise.
The instruction set is published in Ollama’s Modelfile reference, and it changes: REQUIRES, which pins a minimum Ollama version for a model, is a recent addition and is absent from older copies of the same page. Treat any Modelfile guide that does not list it as describing an older release.
FROM decides what the rest means
FROM takes three kinds of argument and the difference is not cosmetic. A registry reference — FROM qwen3:8b — inherits every layer of that model, so your file only has to state what differs. A path to a GGUF file — FROM ./model.gguf — starts from raw weights with no template and no parameters, which means a model built this way will produce fluent nonsense until you supply a TEMPLATE that matches its chat format. A path to a directory of Safetensors weights makes Ollama convert them, which only works for the architectures its converter knows.
The path form is resolved as an absolute path or relative to the Modelfile’s own location, not relative to your shell’s working directory. Building from a different directory than the one the Modelfile sits in is the usual cause of a create that cannot find a file you can see. The GGUF path case is covered end to end in importing a GGUF file into Ollama.
PARAMETER, and the defaults it overrides
Each PARAMETER name value line writes one entry into a params layer that ships with the model, so a user who pulls it gets your sampling settings without knowing they exist. The documented parameters and their defaults, as published in the Modelfile reference:
temperature0.8,top_k40,top_p0.9,min_p0.0 — the sampler. See sampling parameters for what these do to the distribution.repeat_penalty1.1 andrepeat_last_n64 — the repetition control, where 0 disables it and -1 sets the window to the whole context.num_predict-1, meaning generate until a stop condition, andnum_ctx, whose documented default of 2048 is now contradicted by the server’s own VRAM-based choice.seed0, andstop, which is the one parameter you repeat: severalPARAMETER stop "..."lines accumulate into a list rather than overwriting each other.
num_ctx row in the reference has not tracked the server. Ollama’s context-length documentation says the default is chosen from detected VRAM — 4k, 32k or 256k — and setting it in a Modelfile opts that model out of the automatic choice permanently. The mechanism is in what num_ctx actually sets.TEMPLATE, SYSTEM and MESSAGE
TEMPLATE holds a Go text/template that turns a request into the token sequence the model was trained on. Three variables are documented: {{ .System }}, {{ .Prompt }} and {{ .Response }}, and text after {{ .Response }} is omitted while generating. If you inherit from a registry model you almost never write this; if you started from a bare GGUF you must, because the special tokens are model-specific and getting them wrong degrades the model without producing an error.
SYSTEM supplies the string that fills {{ .System }}. It is inert if the template has no such branch — the failure mode behind most “my system prompt is ignored” reports, taken apart in setting a system prompt in a Modelfile. MESSAGE role text prepends example turns with roles system, user or assistant, which is few-shot prompting baked into the model rather than resent by the caller. ADAPTER applies a LoRA — Safetensors directory or GGUF file — and Ollama’s import documentation warns that a mismatched base model gives erratic results rather than an error. LICENSE embeds licence text as its own layer; where weights are gated, that gating is a condition of your access and copying the weights into a public model does not remove it.
Build it, then read it back
A complete file that sets parameters and a system message together:
# Modelfile FROM qwen3:8b PARAMETER temperature 0.2 PARAMETER top_p 0.9 PARAMETER repeat_penalty 1.05 PARAMETER num_ctx 16384 PARAMETER stop "</review>" SYSTEM """You review pull request diffs. Answer with at most five bullet points. Quote the exact line you are objecting to before you object to it. If the diff is fine, say so in one line and stop."""
- Save it as
Modelfilein an empty directory. - Build from that directory:
ollama create reviewer -f Modelfile. The-fflag defaults toModelfile, so the flag is optional when the name matches. - Confirm what was stored, rather than what you typed:
ollama show --parameters reviewerandollama show --system reviewerprint the two layers back. - Run it:
ollama run reviewer, paste a diff, and check that the stop string and the five-bullet instruction both bite.
Two habits make the round trip reliable. Build in a directory that contains nothing but the Modelfile and any local weights it names, so a relative FROM cannot pick up the wrong file. And check with ollama show rather than by reading the model’s answers: a system message that fails to take effect and a system message that the model chose to ignore look identical from the outside, and only one of them is a Modelfile problem.
One detail surprises people on the round trip. ollama show --modelfile does not return the file you wrote: the FROM line comes back as a blob path under the models directory, because the built model references content by digest rather than by tag. That is the storage layout doing its job, and it is why two tags of the same weights cost one copy on disk — where Ollama stores models shows the manifests that make it work.