Hugging Face dry run: budget disk before model downloads

hugging-faceself-hostingopen-sourcestoragetutorial

TL;DR: Use snapshot_download(..., dry_run=True) to inspect a pinned, filtered model download before transferring weights. Count missing file bytes separately from the selected artifact’s total size, and keep a disk reserve. A successful storage preflight does not prove the model fits your RAM or GPU.

What you’ll have after this walkthrough:

  • A reproducible file selection tied to one repository revision.
  • A report distinguishing selected bytes from bytes still needing download.
  • An explicit decision to proceed, narrow the selection, or postpone the download.

Honest take: Inspect the download plan before buying a larger SSD or pulling every format in a model repository.

Why can a model download exceed the size you expected?

A model repository can contain several weight formats, quantizations, exports, and supporting files. Downloading the whole repository is a different operation from downloading the one artifact your runtime needs. The official Hub download guide documents both repository snapshots and file filters.

The first decision is therefore the file set, not the disk purchase. A GGUF-oriented runtime does not automatically need every training checkpoint in the same repository. A Transformers application may need configuration, tokenizer files, weight indexes, and all referenced shards. Selecting too much wastes storage; selecting too little produces an incomplete application.

QuantityWhat it helps decideWhat it cannot establish
Selected artifact bytesSize of the file set you requestedPeak temporary storage during every transfer
Bytes marked for downloadRemaining file payload for this cache stateExact network traffic after transport overhead
Free space on destination filesystemWhether a chosen reserve policy passes nowSpace available after another job starts writing
Runtime RAM or VRAMWhether inference may fitWhether the selected files are complete or licensed

This tutorial tests the free Apache-2.0-licensed huggingface_hub client. Its license does not grant rights to every model it can download. Read the model’s own license before downloading, deploying, or redistributing it.

What does a Hugging Face dry run actually do?

A Hub dry run retrieves download metadata without transferring the selected file payloads. According to the official download reference, snapshot dry runs return per-file information. The client can report the file’s identity, size, cache status, and whether it would be downloaded.

That makes the operation useful before a large transfer, but it is not an offline operation. Public metadata still requires network access. Private or gated repositories have their own access requirements; this tutorial uses a public model and explicitly disables token use in the Python calls.

We checked the client against version 2.4.0, listed on PyPI, and ran the metadata exercise on November 7, 2026. A GPU is unnecessary for this exercise. Python, network access, and a small amount of local disk space are enough to inspect the public configuration file used below.

Create an isolated environment instead of changing an inference server’s existing dependencies:

python3 -m venv .venv-hf-plan
. .venv-hf-plan/bin/activate
python -m pip install 'huggingface_hub==2.4.0'
python -c 'import huggingface_hub; print(huggingface_hub.__version__)'

Expected version output: 2.4.0. This is the tested client snapshot, not a recommendation to upgrade an unrelated production application. Keep the application’s own lock file intact.

How do you pin the revision and choose files?

Resolve the repository’s current commit once, inspect the file selection, and reuse that commit for the eventual transfer. A branch name such as main can move between planning and downloading.

The public example is sentence-transformers/all-MiniLM-L6-v2. Its model card lists an Apache 2.0 license. We select only config.json to test the download mechanism without downloading model weights. That file alone cannot run the model.

Save this as plan_download.py:

import json
import shutil
from pathlib import Path

from huggingface_hub import HfApi, snapshot_download

REPO = "sentence-transformers/all-MiniLM-L6-v2"
PATTERNS = ["config.json"]
CACHE = Path("./hf-plan-cache").resolve()
# A chosen operating reserve, not a Hub hardware requirement.
RESERVE_BYTES = 1_000_000_000


def summarize(files, free_bytes, reserve_bytes):
    if not files:
        raise ValueError("selection matched no files")
    selected = 0
    missing = 0
    for item in files:
        size = item.file_size
        if isinstance(size, bool) or not isinstance(size, int) or size < 0:
            raise ValueError(f"unknown or invalid size: {item.filename}")
        selected += size
        if item.will_download:
            missing += size
    return {
        "selected_bytes": selected,
        "download_file_bytes": missing,
        "free_bytes": free_bytes,
        "reserve_bytes": reserve_bytes,
        "space_policy_pass": free_bytes >= missing + reserve_bytes,
    }


if __name__ == "__main__":
    CACHE.mkdir(parents=True, exist_ok=True)
    revision = HfApi().model_info(REPO, token=False).sha
    if not revision:
        raise SystemExit("repository revision was not returned")
    files = snapshot_download(
        repo_id=REPO,
        revision=revision,
        allow_patterns=PATTERNS,
        cache_dir=CACHE,
        token=False,
        dry_run=True,
    )
    report = summarize(
        files, shutil.disk_usage(CACHE).free, RESERVE_BYTES
    )
    report.update({
        "repo": REPO,
        "revision": revision,
        "patterns": PATTERNS,
        "cache_dir": str(CACHE),
        "files": [
            {"name": f.filename, "bytes": f.file_size,
             "cached": f.is_cached, "will_download": f.will_download}
            for f in files
        ],
    })
    Path("download-plan.json").write_text(
        json.dumps(report, indent=2) + "\n", encoding="utf-8"
    )
    print(json.dumps(report, indent=2))
    if not report["space_policy_pass"]:
        raise SystemExit("insufficient space under the chosen reserve policy")

Run python plan_download.py. The report should name only config.json, record a full revision, and contain actual byte values returned for your run. An empty match or an unknown size stops the calculation. Treating unknown metadata as zero would incorrectly make a large download look safe.

The script creates the cache directory before measuring its filesystem. That matters when your home directory, model disk, and temporary directory live on different mounts. Checking free space on your shell’s current disk is not enough if the payload will land elsewhere.

What did the local cache experiment establish?

We ran a cold-cache dry run, downloaded only the selected configuration file, then repeated the same plan against the same pinned revision and cache. The first plan marked the file for download. After that small transfer, the second plan reported the file cached and no remaining file payload to download.

These observations test metadata selection and cache reuse. They do not test model inference, model quality, a GPU memory requirement, or a large weight transfer. A repository snapshot is not declared usable simply because its configuration file is cached.

We also tested the report function with constructed file records. An empty list failed; an unknown file size failed; a deliberately small free-space allowance failed the reserve policy. The byte accounting used a simple fixture: selected sizes of 100 and 200 bytes, with only the latter marked for download, produced 300 selected bytes and 200 download-file bytes. Those tiny values are synthetic arithmetic, not model file sizes.

How do you download exactly the approved plan?

Reuse the saved revision and patterns instead of resolving main again. This short follow-up reads the plan and performs the transfer only if the saved policy passed:

import json
from pathlib import Path
from huggingface_hub import snapshot_download

plan = json.loads(Path("download-plan.json").read_text(encoding="utf-8"))
if not plan["space_policy_pass"]:
    raise SystemExit("plan did not pass; investigate before downloading")

path = snapshot_download(
    repo_id=plan["repo"],
    revision=plan["revision"],
    allow_patterns=plan["patterns"],
    cache_dir=plan["cache_dir"],
    token=False,
)
print(path)

The returned path identifies a local snapshot location. For this example it contains the selected configuration file, not a runnable model. Before a real weight transfer, repeat the dry run immediately if free space or the cache changed. A saved report is a record of a past check, not a reservation of disk capacity.

Do not add force_download=True casually. A forced transfer changes which files need downloading even when a cached copy exists. Likewise, changing the cache directory changes the plan’s cache assumptions. Plan and transfer need the same relevant arguments.

How much extra disk reserve should you keep?

Choose a reserve for your machine’s other activity, temporary files, and recovery needs. The example’s 1,000,000,000-byte reserve is an explicit policy choice, not an official minimum or a promise that every large download will succeed.

Download-file bytes are not an exact prediction of peak allocated disk usage. Transport caches, incomplete transfers, filesystem behavior, and concurrent jobs can change the peak. If you use local_dir rather than the cache-centered approach shown here, inspect that destination and its metadata behavior separately. Do not assume a backup copy, an export, and a cache snapshot share all storage.

The official cache guide describes cache organization and management. Avoid deleting cache contents while an application or download is using them. Investigate what is reclaimable before removing anything, and keep an independent backup when recovery matters.

For an actual storage purchase, compare the additional space needed with what you can safely reclaim and which format you actually need. This article does not quote an SSD price or claim a payback period. The planning script requires no paid API or hosted subscription; real storage hardware, network service, and electricity remain part of your own setup’s costs.

Can filtering accidentally produce a broken model?

Yes. A filter describes filenames, not a dependency solver. A pattern that retrieves one weight shard but omits the index and remaining shards can pass a byte check while producing a model your runtime cannot load.

Inspect the repository’s file tree and runtime instructions before expanding this example. Include the tokenizer and configuration files required by the selected runtime. For split GGUF artifacts, verify the complete shard set; for other formats, follow the loader’s documented requirements. Do not infer completeness from a filename containing the model name.

The same distinction appears in our model backup and restore walkthrough: copying artifacts is only one stage. A restore or inference test establishes a different property. The kernel compatibility preflight addresses another separate question, whether a software and hardware combination can use a particular kernel.

When should you skip this workflow?

Skip the extra script when you are downloading a small, well-understood artifact and have ample free space. A dry run is most useful for large repositories, multiple formats, constrained disks, metered connections, or repeatable deployment preparation.

Do not use this workflow as a substitute for model-license review or runtime capacity planning. A large MoE can have a manageable active parameter count while still requiring substantial resident weights. File bytes, system memory, and GPU memory answer different questions.

Air-gapped machines also need a different procedure: perform the online planning and transfer on an authorized connected machine, then validate the copied artifact in the target environment. The environment-variable reference documents offline and cache controls, but enabling offline mode cannot conjure files that were never downloaded.

Honest verdict

A pinned dry run is a useful gate before committing bandwidth and storage to a model you have not yet tried. Keep the file list explicit, reject unknown sizes, measure the destination filesystem, and preserve a reserve. Then validate the actual transfer and the intended application. Passing a storage policy earns permission to attempt the next step; it does not prove the entire self-hosted stack works.

Sources

Last updated November 7, 2026. Official sources checked today; client 2.4.0 tested with public metadata and a configuration-only download. No model inference, GPU benchmark, or large-weight transfer is claimed. Verify current files and free space before downloading.

Was this article helpful?

What self-hosting actually costs

Real cost breakdowns for self-hosted AI: hardware floors, power, maintenance hours, and the honest comparison against paying for it. No spam, unsubscribe anytime.