Building a Custom Model With ollama create
9 min read · updated August 11, 2026
ollama create is a compiler with one output format. It reads a Modelfile, resolves the base, turns each instruction into a content-addressed layer, writes a manifest, and gives that manifest a name. Every confusing failure it produces is one of those four stages telling you it could not finish.
What create actually does
The command is ollama create MODEL, where MODEL is the name you will use afterwards. The build resolves FROM first: a registry reference is pulled if absent, a GGUF path is read from disk, a Safetensors directory is converted. Then each remaining instruction becomes its own layer with its own media type — application/vnd.ollama.image.model for the weights, ...image.template, ...image.params, ...image.system, ...image.license — and the manifest lists them by SHA-256 digest.
That digest addressing is why a rebuild after changing one PARAMETER line is nearly instantaneous even for a 5 GB model: the weights layer has the same digest as before, so nothing is copied, and only the tiny params layer is new. It is also why building three variants of one base costs one copy of the weights rather than three. The same property is what makes disk use per model hard to read off ollama list.
Reading the stages backwards makes failures legible. A create that stops while resolving FROM is a missing file, a wrong relative path or an architecture the Safetensors converter does not recognise — Ollama’s import documentation lists the supported families, and a model outside that list has to be converted with llama.cpp’s convert_hf_to_gguf.py before Ollama will look at it. A create that stops while writing layers is nearly always disk: the models directory needs room for the new blob while the old one is still referenced, so a rebuild can momentarily need twice the model’s size. And a create that finishes but produces a model answering with raw special tokens resolved FROM a bare GGUF and inherited no template, which is a Modelfile problem rather than a build problem.
There is an API equivalent, and it is worth knowing exists because every GUI wrapper uses it. POST /api/create takes model plus from, system, template, parameters, messages, license, adapters and quantize as JSON fields rather than as a file, and streams back status lines. Local files are not sent inline: you push each one as a blob first and reference it by digest in files. That is the same content-addressed store the CLI writes into, which is why a model built through the API is indistinguishable from one built on the command line.
The flags, and what each one changes
-f,--file— the Modelfile to read. It defaults toModelfilein the current directory, soollama create reviewerandollama create reviewer -f Modelfileare the same command. Point it elsewhere and remember thatFROMpaths inside the file resolve relative to the file, not to your shell.-q,--quantize— quantize an F16 or F32 base during the build. Ollama’s import documentation lists the supported targets asq8_0,q4_K_Sandq4_K_M. It does nothing useful when the base is already quantized, and requantizing a quantized model compounds the error rather than saving space twice.--draft-quantize— the same idea applied to a draft model for speculative decoding, which only matters if your base declares one.--experimental— enables the newer Safetensors creation path. Flags with this name exist to be removed; checkollama create --helpon your version rather than trusting a blog post.
Build a named model
- Make a directory and put a Modelfile in it. Start from a base you already have, so the first build does not also spend ten minutes pulling weights:
FROM qwen3:8b PARAMETER temperature 0.1 PARAMETER num_ctx 8192 SYSTEM """You translate English to Dutch. Output only the translation. No notes, no alternatives."""
- Build it, from that directory:
ollama create nl-translate. The output names each layer as it is written and ends withwriting manifestandsuccess. Anything that stops beforewriting manifestleft nothing behind but blobs. - Verify the build rather than the intent:
ollama show nl-translateprints architecture, parameter count and quantization level;ollama show --system nl-translateprints the system layer back verbatim. - Run it:
ollama run nl-translate "The kettle is boiling."— a single-shot invocation that loads, answers and leaves the model resident for the keep-alive window. - Iterate. Edit the Modelfile, run
ollama create nl-translateagain with the same name, and the tag moves to the new manifest. The old manifest is replaced, not versioned; if you want both, build under two names.
Quantizing during the build
--quantize only applies when the base is unquantized. The canonical case is a fine-tune you converted from Safetensors: the F16 GGUF is roughly two bytes per parameter, which for an 8B model is around 16 GB, and ollama create --quantize q4_K_M mymodel reduces that to roughly 0.6 bytes per parameter. The arithmetic and the source of those bytes-per-weight figures are in choosing a quantization when you pull; choosing a quantization covers the quality side of the same decision.
Two limits worth knowing before you plan around it. The supported list is short — one 8-bit target and two 4-bit K-quants — so Ollama’s builder is not a substitute for llama.cpp’s full quantization menu, and there is no importance-matrix option, which is what the better community GGUF publishers use. And the quantization happens on your machine at build time, so the F16 file has to exist on disk first: budget for both the input and the output.
Naming, copying and pushing
Names follow the registry convention namespace/model:tag, defaulting to no namespace and the tag latest. A model you intend to publish has to carry your username as the namespace before it can be pushed, and ollama cp mymodel myuser/mymodel is the documented way to rename — it writes a second manifest pointing at the same blobs, so it costs the size of a JSON file rather than the size of the weights. Pushing then requires the machine’s public key to be registered with your account.
Deleting is the mirror image and the part people get wrong. ollama rm mymodel removes a manifest. A blob is only reclaimed once no manifest references it, so removing one of two tags that share weights frees a few kilobytes and not five gigabytes. If you need the space back, remove every tag that references the blob, and check the result on disk rather than in ollama list.