CoreMesh

How to Write an Ollama Modelfile and Build a Custom Model

Write an Ollama Modelfile that sets a system prompt, temperature and num_ctx, build it with ollama create, run it, and fix the common errors.

How to Write an Ollama Modelfile and Build a Custom Model

An Ollama Modelfile is a plain text file that tells ollama create how to build a custom model: the base model to start from, the runtime parameters to bake in, and the system prompt to send with every request. For the most common case, setting a system prompt, a temperature and a context size, the whole file is four instructions long.

Save it as Modelfile, build it with ollama create reviewer -f ./Modelfile, and start it with ollama run reviewer. The sections below walk through that file line by line, list every instruction and parameter with its documented default, show how to pull and edit the Modelfile of a model you already have, and cover the mistakes behind most "ollama modelfile not working" searches.

Ollama Modelfile example with a system prompt, temperature and num_ctx#

Here's the smallest Modelfile that does something useful:

text
FROM llama3.2
# lower temperature means more predictable answers (default is 0.8)
PARAMETER temperature 0.3
# context window in tokens (default is 2048)
PARAMETER num_ctx 8192
SYSTEM """You are a senior backend engineer reviewing code.
Lead with bugs, then performance, then style.
Keep answers under 200 words unless asked for more."""

Line by line:

  • FROM llama3.2 names the base model. It's the only required instruction. You can point it at any model from the Ollama library, a local GGUF file, or a directory of Safetensors weights.
  • PARAMETER temperature 0.3 lowers randomness. The Modelfile reference lists the default as 0.8 as of October 18, 2026.
  • PARAMETER num_ctx 8192 sets the context window, the number of tokens the model can see when it generates the next one. The documented default is 2048, which is small once you paste in code or long documents.
  • SYSTEM """...""" is the system message. Triple quotes let it span several lines. A one-line system prompt can skip the quotes, as in the official example: SYSTEM You are Mario from super mario bros, acting as an assistant.

Lines starting with # are comments. The file isn't case sensitive, and instructions can appear in any order. Uppercase and FROM-first are convention, not requirements.

Build and run the custom model#

bash
ollama create reviewer -f ./Modelfile
ollama run reviewer

ollama create takes the name you want for the new model and the -f path to the Modelfile. ollama run then starts an interactive session with your settings applied. You don't type the system prompt again. It's part of the model now.

To confirm what got built, print the model's Modelfile back out:

bash
ollama show --modelfile reviewer

The custom name works anywhere a model name does. If you call Ollama over HTTP, pass reviewer as the model and the system prompt and parameters come along. The Ollama API examples show the request shapes, and running an Ollama model locally covers getting the base model onto your machine first.

Ollama Modelfile syntax: every instruction#

The format is one instruction per line, INSTRUCTION arguments, with # for comments. As of October 18, 2026 the reference documents these instructions:

Instruction Required What it does
FROM yes Base model: a library model and tag, a GGUF file, or a Safetensors directory
PARAMETER no A runtime parameter such as temperature or num_ctx
TEMPLATE no The full prompt template, in Go template syntax
SYSTEM no The system message inserted into the template
LICENSE no License text to ship with the model
MESSAGE no Example conversation turns with roles system, user or assistant
REQUIRES no Minimum Ollama version, for example REQUIRES 0.14.0
CAPABILITY no Declares an extra capability on a GGUF or MLX model

The FROM line has these documented forms:

text
# an existing model, optionally with a tag
FROM llama3.2:latest

# a single GGUF file, absolute or relative to the Modelfile
FROM ./ollama-model.gguf

# a split GGUF: keep the original shard names and use a wildcard
FROM ./model-*.gguf

# a directory containing Safetensors weights for a supported architecture
FROM ./my-safetensors-model

Relative GGUF paths resolve against the Modelfile's location, not your shell's working directory. Split shards can also be listed on separate FROM lines instead of using a wildcard. If you're downloading GGUF files, the Hugging Face GGUF docs explain the format and how repositories are laid out.

Ollama Modelfile parameters and their defaults#

Every parameter is written as PARAMETER name value, one per line. These are the valid parameters from the reference as of October 18, 2026:

Parameter Type Default What it controls
num_ctx int 2048 Context window size in tokens
temperature float 0.8 Higher is more creative, lower is more coherent
top_k int 40 Keeps only the k most likely tokens; lower is more conservative
top_p float 0.9 Nucleus sampling, works together with top_k
min_p float 0.0 Minimum probability relative to the most likely token
repeat_penalty float 1.0 Penalizes repetition; 1.0 is disabled
repeat_last_n int 64 How far back to look for repeats; 0 disables, -1 means num_ctx
seed int 0 A fixed seed gives the same output for the same prompt
num_predict int -1 Max tokens to generate; -1 is unlimited
stop string none Stop sequence; repeat the line for several
draft_num_predict int 4 (separate draft model) Speculative draft tokens per step; 0 disables

A few notes on using them:

  • Stop sequences take one PARAMETER stop line each. The llama3.2 Modelfile printed by ollama show has four of them. Quote the value: PARAMETER stop "User:".
  • seed plus a low temperature is the combination for repeatable output in tests.
  • num_predict caps the response length. It's handy for a classifier model that should answer with one word.
  • If you only change one thing, make it num_ctx. 2048 tokens fills up fast once a system prompt and some pasted code are in play. I'd start at 8192 for coding assistants. Larger windows use more memory, so go up only as far as you need.

Generate a Modelfile from code#

When you maintain several variants (a strict reviewer, a chatty explainer, a one-word classifier), it's easier to generate Modelfiles than to hand-edit them. This script builds one from a dictionary and rejects parameter names or types that aren't in the documented table:

python
PARAMS = {
    "num_ctx": int, "repeat_last_n": int, "repeat_penalty": float,
    "temperature": float, "seed": int, "stop": str, "num_predict": int,
    "draft_num_predict": int, "top_k": int, "top_p": float, "min_p": float,
}

def modelfile(base, system, params):
    lines = [f"FROM {base}"]
    for name, value in params.items():
        if name not in PARAMS:
            raise ValueError(f"unknown parameter: {name}")
        values = value if isinstance(value, list) else [value]
        for v in values:
            want = PARAMS[name]
            ok = isinstance(v, want) or (want is float and isinstance(v, int))
            if not ok:
                raise TypeError(f"{name} expects {want.__name__}, got {v!r}")
            shown = f'"{v}"' if name == "stop" else v
            lines.append(f"PARAMETER {name} {shown}")
    lines.append(f'SYSTEM """{system}"""')
    return "\n".join(lines) + "\n"

print(modelfile(
    "llama3.2",
    "You are a terse code reviewer. Point out bugs first, style second.",
    {"temperature": 0.2, "num_ctx": 8192, "stop": ["<|eot_id|>", "User:"]},
))

Redirect the output to a file named Modelfile and run ollama create on it as before.

Where the Modelfile is and how to edit an existing one#

Ollama doesn't keep a Modelfile per model on disk for you to open in an editor. The model is stored as blobs under Ollama's models directory, and the Modelfile is reconstructed on demand:

bash
ollama show --modelfile llama3.2 > Modelfile

The output starts with a comment telling you what to do next. In the official example it reads:

text
# Modelfile generated by "ollama show"
# To build a new Modelfile based on this one, replace the FROM line with:
# FROM llama3.2:latest
FROM /Users/pdevine/.ollama/models/blobs/sha256-00e1317cbf74d901080d7100f57580ba8dd8de57203072dc6f668324ba545f29
TEMPLATE """..."""
PARAMETER stop "<|start_header_id|>"
PARAMETER stop "<|end_header_id|>"
PARAMETER stop "<|eot_id|>"
PARAMETER stop "<|reserved_special_token"

So the edit workflow is:

  1. Dump the Modelfile with ollama show --modelfile.
  2. Replace the FROM /.../blobs/sha256-... line with FROM llama3.2:latest as the comment suggests.
  3. Add or change PARAMETER and SYSTEM lines. Leave TEMPLATE and the existing stop lines alone unless you have a reason.
  4. Run ollama create with a new name.

That's also how you modify a custom model you built earlier: show, edit, create again.

TEMPLATE and MESSAGE: when to touch them#

Most Modelfiles don't need a TEMPLATE. The base model already carries one, and the basic example in the reference omits it entirely. You write a template when you're building from raw GGUF or Safetensors weights that don't ship with one, or when the model expects a chat format that differs from what you inherited.

Templates use Go template syntax with three variables:

Variable Meaning
{{ .System }} The system message
{{ .Prompt }} The user's message
{{ .Response }} The model's reply; anything after it is omitted

The reference gives this ChatML-style example:

text
TEMPLATE """{{ if .System }}<|im_start|>system
{{ .System }}<|im_end|>
{{ end }}{{ if .Prompt }}<|im_start|>user
{{ .Prompt }}<|im_end|>
{{ end }}<|im_start|>assistant
"""

The special tokens are model specific. Copy them from the model's own documentation rather than from a different model's template.

MESSAGE is a lighter-weight tool: it seeds the conversation with example turns so the model answers in the same style. The documented example teaches terse yes/no answers:

text
MESSAGE user Is Toronto in Canada?
MESSAGE assistant yes
MESSAGE user Is Sacramento in Canada?
MESSAGE assistant no
MESSAGE user Is Ontario in Canada?
MESSAGE assistant yes

MESSAGE system is an alternate way to set the system prompt. LICENSE """...""" attaches license text, REQUIRES 0.14.0 pins a minimum Ollama version, and CAPABILITY decision declares a capability on GGUF or MLX weights trained for it. Repeat CAPABILITY to add more than one.

Ollama Modelfile not working: common mistakes#

  • No FROM line. It's the only required instruction. Everything else is optional.
  • Wrong path to a GGUF. Relative paths resolve from the Modelfile's location. Use an absolute path when in doubt.
  • A multi-line SYSTEM without triple quotes. Wrap anything longer than one line in """, or only the first line counts.
  • Several stop sequences on one line. Each needs its own PARAMETER stop line.
  • Misspelled parameter names. It's num_ctx, not context_length or num_context. The table above is the full list.
  • Running the base model instead of the new one. ollama run llama3.2 ignores your Modelfile. Run the name you gave to ollama create.
  • Using a blob path from ollama show as FROM. It works on the machine that generated it and nowhere else. Swap it for the model name as the generated comment says.
  • Rewriting TEMPLATE for a library model. The inherited template already matches the model. Changing it changes how every prompt is encoded, so keep it unless the model's documentation says otherwise.

A quick lint before you build catches the first few of these. Paste your Modelfile into the text constant:

javascript
const text = `
from llama3.2
parameter temperature 0.3
PARAMETER context_length 8192
PARAMETER stop "<|eot_id|>"
PARAMETER stop "User:"
SYSTEM """You review code.
Bugs first, then style."""
`;

const instructions = new Set(["FROM", "PARAMETER", "TEMPLATE", "SYSTEM",
  "LICENSE", "MESSAGE", "REQUIRES", "CAPABILITY"]);
const params = new Set(["num_ctx", "repeat_last_n", "repeat_penalty",
  "temperature", "seed", "stop", "num_predict", "draft_num_predict",
  "top_k", "top_p", "min_p"]);

const problems = [];
let hasFrom = false, stops = 0, inBlock = false;

text.split("\n").forEach((raw, i) => {
  const quotes = (raw.match(/"""/g) || []).length;
  if (inBlock) { if (quotes % 2) inBlock = false; return; }
  const line = raw.trim();
  if (!line || line.startsWith("#")) return;
  const [word, ...rest] = line.split(/\s+/);
  const inst = word.toUpperCase(); // the Modelfile is not case sensitive
  if (!instructions.has(inst)) {
    problems.push(`line ${i + 1}: unknown instruction "${word}"`);
  } else if (inst === "FROM") {
    hasFrom = true;
  } else if (inst === "PARAMETER") {
    if (!params.has(rest[0])) problems.push(`line ${i + 1}: unknown parameter "${rest[0]}"`);
    if (rest[0] === "stop") stops++;
  }
  if (quotes % 2) inBlock = true;
});
if (!hasFrom) problems.push("missing FROM, which is required");

console.log(problems.length ? problems.join("\n") : "no problems found");
console.log(`stop sequences: ${stops}`);

Quick reference#

A Modelfile skeleton with the parameters most people set:

text
FROM llama3.2                  # required; model, ./file.gguf, ./shards-*.gguf, or ./safetensors-dir
PARAMETER temperature 0.3      # default 0.8
PARAMETER num_ctx 8192         # default 2048
PARAMETER seed 42              # repeatable output
PARAMETER num_predict 256      # cap response length; -1 = unlimited
PARAMETER stop "User:"         # one line per stop sequence
SYSTEM """multi-line system prompt"""

The commands you'll use:

bash
ollama create reviewer -f ./Modelfile          # build
ollama run reviewer                            # chat
ollama show --modelfile reviewer               # inspect what was built
ollama show --modelfile llama3.2 > Modelfile   # start from an existing model

Before you build, check five things: FROM is present, parameter names match the table, each stop sequence has its own line, a multi-line SYSTEM sits inside triple quotes, and you're running the new name rather than the base model. When something looks different from this page, the Modelfile reference is the place to confirm the current instructions and defaults.

Quick answers

Where is the Ollama Modelfile located?

Ollama doesn't store an editable Modelfile per model. Models live as blobs under Ollama's models directory, and you print the Modelfile on demand with ollama show --modelfile, then redirect it to a file to edit.

What does num_ctx do in an Ollama Modelfile?

It sets the context window in tokens, which is how much text the model can see when generating the next token. The Modelfile reference lists the default as 2048 as of October 18, 2026, so add PARAMETER num_ctx 8192 or higher for long prompts.

How do I edit an existing model's Modelfile?

Run ollama show --modelfile llama3.2 > Modelfile, replace the blob FROM line with FROM llama3.2:latest as the generated comment suggests, change the PARAMETER and SYSTEM lines, then run ollama create with a new name.

Do I need a TEMPLATE in my Modelfile?

Not for a library model. The base model already carries a template, and the basic official example omits it. Write one only for raw GGUF or Safetensors weights, using Go template syntax with .System, .Prompt and .Response.

What is the Ollama Modelfile format?

One instruction per line in the form INSTRUCTION arguments, with # for comments. FROM is the only required instruction, the file isn't case sensitive, and instructions can appear in any order.

Did this solve it?

One tap. No account.

Saved you an hour? Send a tip.

USDC or ETH on Base, Ethereum, Arbitrum and more. Keeps the notes free and the examples running.

Base

Network

0xc783bc5c3fa14cEC3f09A22284bC91C5AF0d59e1
Open in wallet · 5 USDC

On a computer? Scan the code with your phone wallet, or copy the address.

Pick the network your wallet or exchange uses. USDC works on all six; ETH on Base, Ethereum, Arbitrum and Optimism. Don't send on Tron or Solana: this address doesn't exist there. Tips are optional and unlock nothing; every guide stays free.

Discussion

Start the discussion

Indra Wijaya

Builds and runs AI agents, bots and content pipelines, and writes down what works and what broke.

About · Email · RSS

Keep reading

New notes, no spam.

One post at a time, only when there's something worth reading. Follow in any feed reader.

Subscribe via RSS