Ollama memory reports: disk size, loaded size, and VRAM

ollamalocal-llmmemorytroubleshooting

TL;DR: Compare Ollama’s stored model size with the loaded-model report before buying more memory. /api/tags and /api/ps answer different questions, and neither is a complete GPU accounting tool. Save matching snapshots, keep missing values unknown, and reproduce the workload before drawing a hardware conclusion.

What you’ll be able to do:

  • Separate stored bytes, reported loaded bytes, and reported VRAM bytes.
  • Produce a small report without generating another model response.
  • Recognize a stale model tag or incomplete snapshot before comparing memory use.

Honest take: Start with the loaded-model report and the actual workload, then decide whether a hardware upgrade has evidence behind it.

Why does Ollama show different model sizes?

Ollama’s local model inventory and its loaded-model inventory describe different states. The tags endpoint lists available models; the ps endpoint lists models currently loaded into memory. A download completing successfully does not mean its model is resident, and a stored model size is not a promise about the memory required by your next request.

This distinction matters when a machine seems to have enough VRAM on paper. A reader may compare a model’s download size with the GPU capacity, see a smaller number, and assume that an allocation failure proves the driver is broken. That comparison leaves out the runtime configuration. Ollama’s context-length documentation explicitly connects a larger context allocation with higher memory requirements.

Keep the labels attached to the numbers. A value named size has meaning within its endpoint; matching the spelling of a field across endpoints does not make the measurements interchangeable. The upstream API types, checked November 11, 2026, define separate response structures for the local inventory and the process inventory. The process response also includes size_vram and context_length.

The useful question is therefore: which model was loaded, under which settings, when this report was captured? Asking whether the file is smaller than the card is only an initial storage check.

Which numbers should you record before changing hardware?

Record the stored size, the loaded size report, the VRAM size report, and the loaded context independently. Preserve the raw JSON alongside your application settings so that a later comparison does not depend on a screenshot with rounded units.

EvidenceWhat it establishesWhat it does not establish
/api/tags entryA local model name, digest, and stored size reportWhether that model is loaded now
/api/ps entryA loaded model and its reported memory/context fieldsEvery allocation made by every process
Matching digestsThe two entries identify the same reported model digestThat both snapshots were taken atomically
Empty models in /api/psNo loaded entries were reported at capture timeThat the model cannot load
Missing memory fieldThe snapshot lacks that valueA measurement of zero bytes

The server implementation builds the process response from the scheduler’s loaded models. Treat that as an Ollama report, with the scope its name suggests. Do not relabel it as the operating system’s total free VRAM or as a measurement of a different application’s memory.

Likewise, subtracting size_vram from another number is not a complete system-RAM audit. This tutorial deliberately avoids that calculation. An OS monitor, backend logs, and application behavior can add evidence, but they answer additional questions and should keep their own timestamps and labels.

How do you capture the two inventories without another generation?

Use read-only requests to the same Ollama server, close together in time. On the machine hosting a local Ollama service, these commands save the two native API responses:

curl --noproxy '*' --fail --silent --show-error \
  http://127.0.0.1:11434/api/tags -o tags.json
curl --noproxy '*' --fail --silent --show-error \
  http://127.0.0.1:11434/api/ps -o ps.json

The endpoint paths come from Ollama’s official API documentation. --noproxy '*' makes these particular loopback requests bypass a configured HTTP proxy; it does not change the server’s configuration. If your application uses another host, capture that same host through its intended connection rather than silently inspecting an unrelated local installation.

A failed curl command is not a memory result. Check its exit status and response before running the report. Avoid reusing a file left by an earlier successful capture after a later command fails. Save each comparison in its own directory and note the capture time in your experiment notes.

The requests do not ask the server to generate text. They also do not freeze its state: another client can load or unload a model between requests. Capture during a quiet interval when possible. For a changing workload, use repeated, separately named snapshots rather than claiming that two sequential requests form one atomic observation.

Can a small script keep the fields and identities straight?

The following standard-library Python script joins entries by name and only includes stored bytes when the reported digests also match. Save it as memory_report.py, outside your model directory.

It prints raw byte counts to avoid mixing decimal GB with binary GiB. JSON null means unknown. The snapshot cap is this tutorial’s local input policy, not an Ollama response limit; raise it deliberately if your real inventory exceeds it.

import json
import sys
from pathlib import Path

CAP = 2 * 1024 * 1024  # Tutorial snapshot limit, not an Ollama limit.


def unique(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise ValueError("duplicate JSON key")
        result[key] = value
    return result


def load(path):
    with Path(path).open("rb") as stream:
        raw = stream.read(CAP + 1)
    if len(raw) > CAP:
        raise ValueError("snapshot too large")
    data = json.loads(raw, object_pairs_hook=unique)
    if not isinstance(data, dict) or not isinstance(data.get("models"), list):
        raise ValueError("expected an object with a models array")
    result = {}
    for row in data["models"]:
        if not isinstance(row, dict):
            raise ValueError("model entry must be an object")
        name = row.get("name")
        if not isinstance(name, str) or not name or name in result:
            raise ValueError("missing or duplicate model name")
        for field in ("size", "size_vram", "context_length"):
            if field in row and (type(row[field]) is not int or row[field] < 0):
                raise ValueError("invalid " + field)
        result[name] = row
    return result


def report(tags_path, ps_path):
    tags, running = load(tags_path), load(ps_path)
    output = []
    for name, row in sorted(running.items()):
        local = tags.get(name, {})
        digest = row.get("digest")
        matched = isinstance(digest, str) and bool(digest) and digest == local.get("digest")
        output.append({
            "name": name,
            "tag_digest_matches": matched,
            "stored_bytes": local.get("size") if matched else None,
            "loaded_bytes_reported": row.get("size"),
            "vram_bytes_reported": row.get("size_vram"),
            "context_tokens_reported": row.get("context_length"),
        })
    return {"loaded_model_count": len(output), "models": output}


if __name__ == "__main__":
    try:
        if len(sys.argv) != 3:
            raise ValueError("usage: memory_report.py tags.json ps.json")
        print(json.dumps(report(*sys.argv[1:]), indent=2))
    except (OSError, ValueError) as error:
        print("INVALID_SNAPSHOT: " + str(error), file=sys.stderr)
        sys.exit(1)

Run the report against the saved files:

python3 memory_report.py tags.json ps.json

Only loaded entries appear in the output. That is intentional: the report asks what was loaded when /api/ps responded, rather than printing every installed model with an invented zero-memory measurement.

Duplicate names or duplicate JSON keys are refused. A negative count or a Boolean where a byte count belongs is also refused. These are validation choices for this diagnostic, not claims that Ollama normally produces such responses. They prevent a damaged or manually assembled snapshot from turning into a convincing-looking report.

What did the local fixture actually demonstrate?

The report was exercised with Python 3.13.5 on November 11, 2026, using synthetic JSON files. No Ollama model was loaded and no inference, GPU allocation, or speed benchmark was performed.

The complete fixture used fixture:demo, an arbitrary matching digest, and these deliberately chosen values:

Fixture fieldInput bytes or tokensInterpretation in this test
Stored size3,221,225,472 bytesExactly 3 GiB of synthetic input
Loaded size4,294,967,296 bytesExactly 4 GiB of synthetic input
Reported VRAM2,147,483,648 bytesExactly 2 GiB of synthetic input
Context length4,096 tokensA chosen fixture value, not a recommendation

The result contained loaded_model_count: 1 and preserved all four values in their separate fields. The byte arithmetic uses 1 GiB = 1,073,741,824 bytes. These numbers demonstrate labeling and conversion only; they describe no real model’s memory footprint.

Changing just the running digest made stored_bytes become null, while retaining the process values. Removing the optional memory fields preserved them as null. An empty running list produced loaded_model_count: 0. Invalid JSON, duplicate keys, duplicate names, negative values, Boolean sizes, and an oversized snapshot were rejected.

That last distinction is useful during troubleshooting: a parser refusing input is different from a valid empty inventory. Do not translate both cases into “the GPU has no model.” Keep the diagnostic error with the original snapshot and correct the capture before repeating the interpretation.

What should you do when the numbers still look wrong?

First resolve identity and timing, then vary a single workload setting. If the digests differ, the local tag may have changed or you may be comparing the wrong captures. Recapture from the same server; do not manufacture a stored-size comparison by joining unrelated entries.

When the loaded list is empty, repeat the observation after your normal application has intentionally loaded the intended model. An empty list alone says nothing about maximum capacity. If the model repeatedly unloads between requests, use the separate keep-alive and model-reloading guide to investigate residency policy.

If the model is loaded but your workload fails as context grows, retain the model identity and compare the relevant context setting. Ollama’s context documentation recommends checking the loaded allocation with ollama ps. Change context deliberately, repeat the same input, and keep both observations. A successful shorter input can justify a smaller-context workflow; it does not prove that the longer workload will fit.

For a CPU-only or mixed placement symptom, follow the GPU detection troubleshooting guide. The two snapshots here do not diagnose driver compatibility or guarantee that a supported backend is functioning. Hardware purchases are a poor first response to an unidentified backend problem.

Honest take: when is this enough to make a buying decision?

The snapshot report is enough to correct a mislabeled comparison. It is not enough to size a new workstation by itself. A buying decision needs the intended model, useful context, actual concurrent workload, and a successful or failed reproduction under those conditions.

This diagnostic has no paid service requirement and its parser needs no GPU. Running the model you ultimately care about remains a separate hardware task. If you already own the machine, gather the evidence before assigning a purchase budget. If you do not own suitable hardware, a downloaded model file and somebody else’s screenshot still do not establish your application’s memory requirement.

Keep the full input snapshots and your settings with the result. That small record is more useful than a single rounded “model size” number when deciding whether to reduce context, change the workload, investigate the backend, or start a hardware comparison.

Sources

Last updated November 11, 2026. Official contracts were checked that day; fixture results validate this parser, not model memory requirements or inference performance.

Was this article helpful?

Get the numbers before you buy

New GPU and mini-PC benchmarks, VRAM thresholds, and price checks — sent when there's something worth acting on, not on a schedule. No spam, unsubscribe anytime.