{
  "@context": "https://schema.org",
  "@id": "https://aggr.aymericbeaumet.com/items/huggingface-co/2026-07-07-run-ai-workloads-on-any-cloud-store-on-hugging-face-zero/#webpage",
  "@type": [
    "WebPage",
    "ArchiveComponent"
  ],
  "articleBody": "For most teams, models and datasets live in a bucket in one region of one cloud. The GPUs you can get, whether for development, training, or serving, increasingly sit on a different cloud than your data. The moment those two come apart, you pay a cross-cloud transfer tax just to read your own data onto your own GPUs.\n\nTogether with Hugging Face, we've joined the two halves: your models and datasets stay on the Hub, and SkyPilot runs the compute (dev, training, or serving) on whatever cluster has the GPUs. Mount a Hugging Face Bucket or any Hub repo into a SkyPilot job with one `hf://` URL and the `HF_TOKEN` you already have, then launch it wherever capacity is. Hugging Face charges no egress, so reading your data onto those GPUs costs nothing, on any cloud.\n\nHere's what's new:\n\n- **Your Hub data in any job.** `store: hf` mounts a Hugging Face **Bucket** (read-write) or any **model / dataset / Space repo** (read-only) into a SkyPilot task with one `hf://` URL and your existing `HF_TOKEN`, via `MOUNT` or `COPY`.\n- **Run it on any GPU, on any cloud.** [SkyPilot](https://docs.skypilot.co/) finds that job compute across 20+ clouds, Kubernetes, Slurm, and on-prem, so the same run uses whichever of your reserved or on-demand GPUs is available, on any vendor.\n- **No egress to read your data.** Hugging Face Storage charges [no egress or CDN fees](https://huggingface.co/pricing), so wherever SkyPilot lands the job, it reads your models and datasets straight from the same bucket, with no per-cloud copies and no egress bill to pull them in.\n- **Xet-backed dedup.** Buckets are built on [Xet](https://huggingface.co/docs/hub/xet/overview), so incremental checkpoints and model variants only store and transfer the chunks that changed.\n- **Built together.** [Hugging Face](https://huggingface.co/) and [SkyPilot](https://docs.skypilot.co/) shipped this jointly, and the Hugging Face team upstreamed the `hf-mount` FUSE fixes that make it work in unprivileged containers.\n\n## Hugging Face Storage is now a first-class SkyPilot backend\n\n[![SkyPilot mounts Hugging Face models, datasets, and checkpoints into jobs running on reserved GPU clusters across CoreWeave, Nebius, GCP, and 20+ more, with zero-egress reads.](https://huggingface.co/blog/assets/skypilot-hf-storage/architecture.png)](https://huggingface.co/blog/assets/skypilot-hf-storage/architecture.png)\n\nSkyPilot tasks already read and write cloud object stores (S3, GCS, Azure, R2, and many more) by mounting them at a local path. Hugging Face Storage now joins that list as `store: hf`, reached through the `hf://` scheme:\n\n```yaml\nfile_mounts:\n  # A Hugging Face Bucket, read-write, for checkpoints, logs, processed data.\n  /checkpoints:\n    source: hf://buckets/my-org/qwen-sft\n    store: hf\n    mode: MOUNT # or COPY\n  # A model repo, mounted read-only.\n  /base-model:\n    source: hf://Qwen/Qwen3.5-4B\n    store: hf\n    mode: MOUNT\n  # A dataset repo, pinned to a revision, read-only.\n  /data:\n    source: hf://datasets/my-org/my-dataset@main\n    store: hf\n    mode: MOUNT\n```\n\nThat one `hf://` scheme covers the whole lifecycle: read the **model** and **dataset** from their repos, write **checkpoints** to a Bucket while you train, publish the finished model back to a repo, and pull it onto inference servers when you serve. Most teams already keep their models and datasets on the Hub, so there is no migration step and no new storage account to create.\n\n`MOUNT` uses Hugging Face's [`hf-mount`](https://github.com/huggingface/hf-mount) FUSE backend, so a bucket or repo shows up as a local path next to SkyPilot's other FUSE mounts (`gcsfuse`, `blobfuse2`, `rclone`, `goofys`). The fetching happens at the filesystem layer: when your code issues a `read()`, the driver pulls just those bytes from the Xet backend, so only the data you actually touch crosses the network, and `hf-mount` keeps an on-disk cache so repeat reads stay local. That on-disk cache is the behavior SkyPilot gives its other backends under [`MOUNT_CACHED`](https://docs.skypilot.co/en/latest/reference/storage.html), where a plain `MOUNT` instead streams every read from the bucket with nothing kept locally. For the `hf` store, `MOUNT` and `MOUNT_CACHED` behave the same, so either mode keeps the cache.\n\nBecause reads are lazy, a process can start working through a large file before the whole file has downloaded, instead of blocking on a full copy first. That keeps the GPU busy almost immediately, training on data as it streams in rather than sitting idle (and billing) while a dataset or checkpoint copies down. It pays off most on the first epoch, when nothing is cached yet. `COPY` takes the other route and downloads through `huggingface_hub` up front, with no special requirements.\n\nAuthentication is the token you already have. Set `HF_TOKEN` in your environment and hand it to a run with [`--secret HF_TOKEN`](https://docs.skypilot.co/en/latest/running-jobs/environment-variables.html); SkyPilot uses it for the mount on whatever cloud the job lands. One token works whether the job lands on AWS, GCP, Azure, Nebius, Lambda, or your own Kubernetes cluster, so there are no per-cloud bucket keys to juggle.\n\n## No egress: storage stops deciding where you run\n\nGPU capacity rarely comes from one place anymore. To get enough H100s and H200s, teams hold reserved and committed capacity across several vendors at once (a block on a hyperscaler, a cluster on a neocloud, maybe an on-prem rack) and run wherever they have allocation. SkyPilot is built for this: one job spec, scheduled across 20+ clouds, Kubernetes, and on-prem, landing on whichever reserved cluster is free.\n\nObject storage has been the catch. Object stores are regional and per-cloud, so feeding a GPU or an inference server that sits in a different vendor's data center means either keeping a copy of your data in every vendor's bucket or paying to pull it across. Most clouds charge egress (around $0.09/GB out of AWS) the moment data leaves their network, and often between regions inside one cloud. Pulling a base model onto every inference node, or iterating a dataset for several epochs from a cluster on another cloud, adds a hefty bill on top of GPUs you have already reserved. Teams end up pinning each run to whichever vendor holds the data and leaving the rest of their capacity idle.\n\nHugging Face Storage takes that cost off the table where it bites: the read side. With [no egress or CDN fees](https://huggingface.co/pricing) and storage at $12-18/TB/month (versus AWS S3 at roughly $23/TB plus egress), the same bucket is reachable from every one of those clusters, and reading from it is free no matter where the GPUs run. Writing back still costs your compute cloud's usual egress, the same as it would to any off-cloud store, but for most AI work the reads dominate: a dataset streamed over many epochs, or model weights pulled onto every new training or inference node. So you stop pinning each run to whichever vendor holds a copy of the data.\n\n## A quick benchmark\n\nTo collect some benchmark numbers, we ran a small fine-tune: [`Qwen/Qwen3.5-4B`](https://huggingface.co/Qwen/Qwen3.5-4B) on the [`HuggingFaceH4/Multilingual-Thinking`](https://huggingface.co/datasets/HuggingFaceH4/Multilingual-Thinking) dataset with TRL's [`SFTTrainer`](https://huggingface.co/docs/trl/sft_trainer), mounting the model read-only from its Hub repo and writing every checkpoint to a Hugging Face Bucket. The same SkyPilot YAML ran on AWS, GCP, and Lambda, changing only `--infra`. SkyPilot placed each job wherever GPUs were free, and all three read and wrote the same bucket.\n\n```yaml\n# qwen-sft.yaml. Launch anywhere: sky launch qwen-sft.yaml --infra aws|gcp|...\nresources:\n  accelerators: H100:1 # or whatever the cloud has\n\nfile_mounts:\n  /base-model:\n    source: hf://Qwen/Qwen3.5-4B # read-only, lazy-mounted from the Hub\n    store: hf\n    mode: MOUNT\n  /checkpoints:\n    source: hf://buckets/my-org/qwen-sft # read-write Bucket\n    store: hf\n    mode: MOUNT\n\nrun: |\n  python train.py --model /base-model --output_dir /checkpoints\n```\n\nWhat we measured:\n\n- **The model loaded free on every cloud.** Lazy reads pull only what `from_pretrained` touches, so it was ready to train in about 30 seconds (up to ~500 MB/s). Because Hugging Face charges no egress, that pull cost nothing; had the model lived in S3, every read to a GPU on another cloud would have been billed egress (~$0.09/GB on AWS).\n- **Checkpoints streamed straight to the bucket** at up to ~170 MB/s (8.43 GB of weights each) and persisted past the GPU instance.\n\nPer cloud, checkpoints wrote to the bucket at:\n\n| Cloud              | GPU  | Checkpoint write |\n| ------------------ | ---- | ---------------- |\n| AWS (us-east-2)    | L40S | \\~168 MB/s       |\n| GCP (us-central1)  | L4   | \\~123 MB/s       |\n| Lambda (us-west-3) | H100 | \\~112 MB/s       |\n\n## Xet-backed storage: dedup for checkpoints and model variants\n\nHugging Face Buckets are built on [Xet](https://huggingface.co/docs/hub/xet/overview), which uses [content-defined chunking](https://huggingface.co/docs/hub/xet/deduplication) to split files into ~64 KB chunks and store each unique chunk once. Because the boundaries follow the content, an edit changes only the chunks it touches and the rest are recognized as already stored. This pays off in a few places:\n\n- **Incremental and adapter checkpoints.** When you freeze layers, train adapters, or otherwise leave most weights untouched between saves, only the changed chunks upload instead of the whole checkpoint.\n- **Model variants that share a base.** Fine-tunes and quantizations of one base model overlap heavily, so the shared chunks are stored once across all of them.\n- **Datasets you append to.** Logs like conversation traces or inference outputs grow by appending rows to large Parquet files. The existing row groups stay byte-identical, so only the new rows transfer: in Hugging Face's [test](https://huggingface.co/blog/parquet-cdc), appending 10K rows to a 100K-row table moved about 10 MB instead of the full ~106 MB. (If you edit or delete rows in place, write with `use_content_defined_chunking=True` to keep changes local.)\n- **Re-uploads skip what's already stored.** In our test, re-uploading an 8.43 GB blob already in the bucket took about 8 seconds, versus 24 seconds for the first upload, because only chunk hashes move. The same mechanism lets server-side `hf buckets cp` between repos and buckets copy by reference instead of re-uploading bytes.\n\nHow much you save depends on how much your artifacts overlap, but the deduplication is automatic: you write a checkpoint as usual, and only the new chunks leave the machine.\n\n## Get started\n\n```bash\npip install \"skypilot[huggingface]\"\nhf auth login  # or: export HF_TOKEN=<your-token>\n```\n\nAdd an `hf://` mount to any SkyPilot task and launch. `MOUNT` needs a base image with glibc 2.34+ and `/dev/fuse`.\n\n## Built together: Hugging Face and SkyPilot\n\nThe initial `store: hf` support [started as a contribution](https://github.com/skypilot-org/skypilot/pull/9418) from Nikhil Jha. The Hugging Face team [carried it forward](https://github.com/skypilot-org/skypilot/pull/9698) and upstreamed the `hf-mount` FUSE fixes that let it mount in unprivileged containers, the default on many Kubernetes clusters. The SkyPilot team wired it into the storage backend. The whole path is open source: SkyPilot, Hugging Face's `hf-mount`, and the `huggingface_hub` client.\n\n## Resources\n\n- [SkyPilot storage docs](https://docs.skypilot.co/en/latest/reference/storage.html)\n- [Hugging Face Storage Buckets guide](https://huggingface.co/docs/hub/storage-buckets)\n- [`hf-mount`](https://github.com/huggingface/hf-mount)\n- [Xet: content-defined chunking and deduplication](https://huggingface.co/docs/hub/xet/deduplication)\n- [SkyPilot Slack community](https://slack.skypilot.co/)\n",
  "authors": [],
  "captured_at": "2026-10-19T01:26:53.998049939+00:00",
  "category": "ai",
  "content_html": "<p>For most teams, models and datasets live in a bucket in one region of one cloud. The GPUs you can get, whether for development, training, or serving, increasingly sit on a different cloud than your data. The moment those two come apart, you pay a cross-cloud transfer tax just to read your own data onto your own GPUs.</p>\n<p>Together with Hugging Face, we've joined the two halves: your models and datasets stay on the Hub, and SkyPilot runs the compute (dev, training, or serving) on whatever cluster has the GPUs. Mount a Hugging Face Bucket or any Hub repo into a SkyPilot job with one <code>hf://</code> URL and the <code>HF_TOKEN</code> you already have, then launch it wherever capacity is. Hugging Face charges no egress, so reading your data onto those GPUs costs nothing, on any cloud.</p>\n<p>Here's what's new:</p>\n<ul>\n<li><strong>Your Hub data in any job.</strong> <code>store: hf</code> mounts a Hugging Face <strong>Bucket</strong> (read-write) or any <strong>model / dataset / Space repo</strong> (read-only) into a SkyPilot task with one <code>hf://</code> URL and your existing <code>HF_TOKEN</code>, via <code>MOUNT</code> or <code>COPY</code>.</li>\n<li><strong>Run it on any GPU, on any cloud.</strong> <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://docs.skypilot.co/\">SkyPilot</a> finds that job compute across 20+ clouds, Kubernetes, Slurm, and on-prem, so the same run uses whichever of your reserved or on-demand GPUs is available, on any vendor.</li>\n<li><strong>No egress to read your data.</strong> Hugging Face Storage charges <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/pricing\">no egress or CDN fees</a>, so wherever SkyPilot lands the job, it reads your models and datasets straight from the same bucket, with no per-cloud copies and no egress bill to pull them in.</li>\n<li><strong>Xet-backed dedup.</strong> Buckets are built on <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/hub/xet/overview\">Xet</a>, so incremental checkpoints and model variants only store and transfer the chunks that changed.</li>\n<li><strong>Built together.</strong> <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/\">Hugging Face</a> and <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://docs.skypilot.co/\">SkyPilot</a> shipped this jointly, and the Hugging Face team upstreamed the <code>hf-mount</code> FUSE fixes that make it work in unprivileged containers.</li>\n</ul>\n<h2>Hugging Face Storage is now a first-class SkyPilot backend</h2>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/assets/skypilot-hf-storage/architecture.png\"><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://huggingface.co/blog/assets/skypilot-hf-storage/architecture.png\" alt=\"SkyPilot mounts Hugging Face models, datasets, and checkpoints into jobs running on reserved GPU clusters across CoreWeave, Nebius, GCP, and 20+ more, with zero-egress reads.\" loading=\"eager\" decoding=\"async\" fetchpriority=\"high\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></a></p>\n<p>SkyPilot tasks already read and write cloud object stores (S3, GCS, Azure, R2, and many more) by mounting them at a local path. Hugging Face Storage now joins that list as <code>store: hf</code>, reached through the <code>hf://</code> scheme:</p>\n<pre><code class=\"language-yaml\"><span class=\"code-snippet\" data-language=\"YAML\"><span class=\"syntax-source syntax-yaml\"><span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">file_mounts</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span>\n  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-yaml\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-line syntax-number-sign syntax-yaml\">#</span> A Hugging Face Bucket, read-write, for checkpoints, logs, processed data.\n</span>  <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">/checkpoints</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span>\n    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">source</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">hf://buckets/my-org/qwen-sft</span>\n    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">store</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">hf</span>\n    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">mode</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">MOUNT</span> <span class=\"syntax-comment syntax-line syntax-number-sign syntax-yaml\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-line syntax-number-sign syntax-yaml\">#</span> or COPY\n</span>  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-yaml\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-line syntax-number-sign syntax-yaml\">#</span> A model repo, mounted read-only.\n</span>  <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">/base-model</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span>\n    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">source</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">hf://Qwen/Qwen3.5-4B</span>\n    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">store</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">hf</span>\n    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">mode</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">MOUNT</span>\n  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-yaml\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-line syntax-number-sign syntax-yaml\">#</span> A dataset repo, pinned to a revision, read-only.\n</span>  <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">/data</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span>\n    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">source</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">hf://datasets/my-org/my-dataset@main</span>\n    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">store</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">hf</span>\n    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">mode</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">MOUNT</span>\n</span></span></code></pre>\n<p>That one <code>hf://</code> scheme covers the whole lifecycle: read the <strong>model</strong> and <strong>dataset</strong> from their repos, write <strong>checkpoints</strong> to a Bucket while you train, publish the finished model back to a repo, and pull it onto inference servers when you serve. Most teams already keep their models and datasets on the Hub, so there is no migration step and no new storage account to create.</p>\n<p><code>MOUNT</code> uses Hugging Face's <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/huggingface/hf-mount\"><code>hf-mount</code></a> FUSE backend, so a bucket or repo shows up as a local path next to SkyPilot's other FUSE mounts (<code>gcsfuse</code>, <code>blobfuse2</code>, <code>rclone</code>, <code>goofys</code>). The fetching happens at the filesystem layer: when your code issues a <code>read()</code>, the driver pulls just those bytes from the Xet backend, so only the data you actually touch crosses the network, and <code>hf-mount</code> keeps an on-disk cache so repeat reads stay local. That on-disk cache is the behavior SkyPilot gives its other backends under <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://docs.skypilot.co/en/latest/reference/storage.html\"><code>MOUNT_CACHED</code></a>, where a plain <code>MOUNT</code> instead streams every read from the bucket with nothing kept locally. For the <code>hf</code> store, <code>MOUNT</code> and <code>MOUNT_CACHED</code> behave the same, so either mode keeps the cache.</p>\n<p>Because reads are lazy, a process can start working through a large file before the whole file has downloaded, instead of blocking on a full copy first. That keeps the GPU busy almost immediately, training on data as it streams in rather than sitting idle (and billing) while a dataset or checkpoint copies down. It pays off most on the first epoch, when nothing is cached yet. <code>COPY</code> takes the other route and downloads through <code>huggingface_hub</code> up front, with no special requirements.</p>\n<p>Authentication is the token you already have. Set <code>HF_TOKEN</code> in your environment and hand it to a run with <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://docs.skypilot.co/en/latest/running-jobs/environment-variables.html\"><code>--secret HF_TOKEN</code></a>; SkyPilot uses it for the mount on whatever cloud the job lands. One token works whether the job lands on AWS, GCP, Azure, Nebius, Lambda, or your own Kubernetes cluster, so there are no per-cloud bucket keys to juggle.</p>\n<h2>No egress: storage stops deciding where you run</h2>\n<p>GPU capacity rarely comes from one place anymore. To get enough H100s and H200s, teams hold reserved and committed capacity across several vendors at once (a block on a hyperscaler, a cluster on a neocloud, maybe an on-prem rack) and run wherever they have allocation. SkyPilot is built for this: one job spec, scheduled across 20+ clouds, Kubernetes, and on-prem, landing on whichever reserved cluster is free.</p>\n<p>Object storage has been the catch. Object stores are regional and per-cloud, so feeding a GPU or an inference server that sits in a different vendor's data center means either keeping a copy of your data in every vendor's bucket or paying to pull it across. Most clouds charge egress (around $0.09/GB out of AWS) the moment data leaves their network, and often between regions inside one cloud. Pulling a base model onto every inference node, or iterating a dataset for several epochs from a cluster on another cloud, adds a hefty bill on top of GPUs you have already reserved. Teams end up pinning each run to whichever vendor holds the data and leaving the rest of their capacity idle.</p>\n<p>Hugging Face Storage takes that cost off the table where it bites: the read side. With <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/pricing\">no egress or CDN fees</a> and storage at $12-18/TB/month (versus AWS S3 at roughly $23/TB plus egress), the same bucket is reachable from every one of those clusters, and reading from it is free no matter where the GPUs run. Writing back still costs your compute cloud's usual egress, the same as it would to any off-cloud store, but for most AI work the reads dominate: a dataset streamed over many epochs, or model weights pulled onto every new training or inference node. So you stop pinning each run to whichever vendor holds a copy of the data.</p>\n<h2>A quick benchmark</h2>\n<p>To collect some benchmark numbers, we ran a small fine-tune: <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/Qwen/Qwen3.5-4B\"><code>Qwen/Qwen3.5-4B</code></a> on the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/datasets/HuggingFaceH4/Multilingual-Thinking\"><code>HuggingFaceH4/Multilingual-Thinking</code></a> dataset with TRL's <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/trl/sft_trainer\"><code>SFTTrainer</code></a>, mounting the model read-only from its Hub repo and writing every checkpoint to a Hugging Face Bucket. The same SkyPilot YAML ran on AWS, GCP, and Lambda, changing only <code>--infra</code>. SkyPilot placed each job wherever GPUs were free, and all three read and wrote the same bucket.</p>\n<pre><code class=\"language-yaml\"><span class=\"code-snippet\" data-language=\"YAML\"><span class=\"syntax-source syntax-yaml\"><span class=\"syntax-comment syntax-line syntax-number-sign syntax-yaml\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-line syntax-number-sign syntax-yaml\">#</span> qwen-sft.yaml. Launch anywhere: sky launch qwen-sft.yaml --infra aws|gcp|...\n</span><span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">resources</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span>\n  <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">accelerators</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">H100:1</span> <span class=\"syntax-comment syntax-line syntax-number-sign syntax-yaml\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-line syntax-number-sign syntax-yaml\">#</span> or whatever the cloud has\n</span>\n<span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">file_mounts</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span>\n  <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">/base-model</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span>\n    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">source</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">hf://Qwen/Qwen3.5-4B</span> <span class=\"syntax-comment syntax-line syntax-number-sign syntax-yaml\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-line syntax-number-sign syntax-yaml\">#</span> read-only, lazy-mounted from the Hub\n</span>    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">store</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">hf</span>\n    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">mode</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">MOUNT</span>\n  <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">/checkpoints</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span>\n    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">source</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">hf://buckets/my-org/qwen-sft</span> <span class=\"syntax-comment syntax-line syntax-number-sign syntax-yaml\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-line syntax-number-sign syntax-yaml\">#</span> read-write Bucket\n</span>    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">store</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">hf</span>\n    <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">mode</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\">MOUNT</span>\n\n<span class=\"syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml\"><span class=\"syntax-entity syntax-name syntax-tag syntax-yaml\">run</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-mapping syntax-yaml\">:</span> <span class=\"syntax-keyword syntax-control syntax-flow syntax-block-scalar syntax-literal syntax-yaml\">|</span>\n<span class=\"syntax-string syntax-unquoted syntax-block syntax-yaml\">  python train.py --model /base-model --output_dir /checkpoints\n</span></span></span></code></pre>\n<p>What we measured:</p>\n<ul>\n<li><strong>The model loaded free on every cloud.</strong> Lazy reads pull only what <code>from_pretrained</code> touches, so it was ready to train in about 30 seconds (up to <del>500 MB/s). Because Hugging Face charges no egress, that pull cost nothing; had the model lived in S3, every read to a GPU on another cloud would have been billed egress (</del>$0.09/GB on AWS).</li>\n<li><strong>Checkpoints streamed straight to the bucket</strong> at up to ~170 MB/s (8.43 GB of weights each) and persisted past the GPU instance.</li>\n</ul>\n<p>Per cloud, checkpoints wrote to the bucket at:</p>\n<table>\n<thead>\n<tr>\n<th>Cloud</th>\n<th>GPU</th>\n<th>Checkpoint write</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>AWS (us-east-2)</td>\n<td>L40S</td>\n<td>~168 MB/s</td>\n</tr>\n<tr>\n<td>GCP (us-central1)</td>\n<td>L4</td>\n<td>~123 MB/s</td>\n</tr>\n<tr>\n<td>Lambda (us-west-3)</td>\n<td>H100</td>\n<td>~112 MB/s</td>\n</tr>\n</tbody>\n</table>\n<h2>Xet-backed storage: dedup for checkpoints and model variants</h2>\n<p>Hugging Face Buckets are built on <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/hub/xet/overview\">Xet</a>, which uses <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/hub/xet/deduplication\">content-defined chunking</a> to split files into ~64 KB chunks and store each unique chunk once. Because the boundaries follow the content, an edit changes only the chunks it touches and the rest are recognized as already stored. This pays off in a few places:</p>\n<ul>\n<li><strong>Incremental and adapter checkpoints.</strong> When you freeze layers, train adapters, or otherwise leave most weights untouched between saves, only the changed chunks upload instead of the whole checkpoint.</li>\n<li><strong>Model variants that share a base.</strong> Fine-tunes and quantizations of one base model overlap heavily, so the shared chunks are stored once across all of them.</li>\n<li><strong>Datasets you append to.</strong> Logs like conversation traces or inference outputs grow by appending rows to large Parquet files. The existing row groups stay byte-identical, so only the new rows transfer: in Hugging Face's <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/parquet-cdc\">test</a>, appending 10K rows to a 100K-row table moved about 10 MB instead of the full ~106 MB. (If you edit or delete rows in place, write with <code>use_content_defined_chunking=True</code> to keep changes local.)</li>\n<li><strong>Re-uploads skip what's already stored.</strong> In our test, re-uploading an 8.43 GB blob already in the bucket took about 8 seconds, versus 24 seconds for the first upload, because only chunk hashes move. The same mechanism lets server-side <code>hf buckets cp</code> between repos and buckets copy by reference instead of re-uploading bytes.</li>\n</ul>\n<p>How much you save depends on how much your artifacts overlap, but the deduplication is automatic: you write a checkpoint as usual, and only the new chunks leave the machine.</p>\n<h2>Get started</h2>\n<pre><code class=\"language-bash\"><span class=\"code-snippet\" data-language=\"Shell\"><span class=\"syntax-source syntax-shell syntax-bash\"><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">pip</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> install <span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span>skypilot[huggingface]<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span></span>\n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">hf</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> auth login  <span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell\">#</span></span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\"> or: export HF_TOKEN=&lt;your-token&gt;</span><span class=\"syntax-comment syntax-line syntax-number-sign syntax-shell\">\n</span></span></span></span></code></pre>\n<p>Add an <code>hf://</code> mount to any SkyPilot task and launch. <code>MOUNT</code> needs a base image with glibc 2.34+ and <code>/dev/fuse</code>.</p>\n<h2>Built together: Hugging Face and SkyPilot</h2>\n<p>The initial <code>store: hf</code> support <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/skypilot-org/skypilot/pull/9418\">started as a contribution</a> from Nikhil Jha. The Hugging Face team <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/skypilot-org/skypilot/pull/9698\">carried it forward</a> and upstreamed the <code>hf-mount</code> FUSE fixes that let it mount in unprivileged containers, the default on many Kubernetes clusters. The SkyPilot team wired it into the storage backend. The whole path is open source: SkyPilot, Hugging Face's <code>hf-mount</code>, and the <code>huggingface_hub</code> client.</p>\n<h2>Resources</h2>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://docs.skypilot.co/en/latest/reference/storage.html\">SkyPilot storage docs</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/hub/storage-buckets\">Hugging Face Storage Buckets guide</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/huggingface/hf-mount\"><code>hf-mount</code></a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/hub/xet/deduplication\">Xet: content-defined chunking and deduplication</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://slack.skypilot.co/\">SkyPilot Slack community</a></li>\n</ul>\n",
  "content_markdown": "For most teams, models and datasets live in a bucket in one region of one cloud. The GPUs you can get, whether for development, training, or serving, increasingly sit on a different cloud than your data. The moment those two come apart, you pay a cross-cloud transfer tax just to read your own data onto your own GPUs.\n\nTogether with Hugging Face, we've joined the two halves: your models and datasets stay on the Hub, and SkyPilot runs the compute (dev, training, or serving) on whatever cluster has the GPUs. Mount a Hugging Face Bucket or any Hub repo into a SkyPilot job with one `hf://` URL and the `HF_TOKEN` you already have, then launch it wherever capacity is. Hugging Face charges no egress, so reading your data onto those GPUs costs nothing, on any cloud.\n\nHere's what's new:\n\n- **Your Hub data in any job.** `store: hf` mounts a Hugging Face **Bucket** (read-write) or any **model / dataset / Space repo** (read-only) into a SkyPilot task with one `hf://` URL and your existing `HF_TOKEN`, via `MOUNT` or `COPY`.\n- **Run it on any GPU, on any cloud.** [SkyPilot](https://docs.skypilot.co/) finds that job compute across 20+ clouds, Kubernetes, Slurm, and on-prem, so the same run uses whichever of your reserved or on-demand GPUs is available, on any vendor.\n- **No egress to read your data.** Hugging Face Storage charges [no egress or CDN fees](https://huggingface.co/pricing), so wherever SkyPilot lands the job, it reads your models and datasets straight from the same bucket, with no per-cloud copies and no egress bill to pull them in.\n- **Xet-backed dedup.** Buckets are built on [Xet](https://huggingface.co/docs/hub/xet/overview), so incremental checkpoints and model variants only store and transfer the chunks that changed.\n- **Built together.** [Hugging Face](https://huggingface.co/) and [SkyPilot](https://docs.skypilot.co/) shipped this jointly, and the Hugging Face team upstreamed the `hf-mount` FUSE fixes that make it work in unprivileged containers.\n\n## Hugging Face Storage is now a first-class SkyPilot backend\n\n[![SkyPilot mounts Hugging Face models, datasets, and checkpoints into jobs running on reserved GPU clusters across CoreWeave, Nebius, GCP, and 20+ more, with zero-egress reads.](https://huggingface.co/blog/assets/skypilot-hf-storage/architecture.png)](https://huggingface.co/blog/assets/skypilot-hf-storage/architecture.png)\n\nSkyPilot tasks already read and write cloud object stores (S3, GCS, Azure, R2, and many more) by mounting them at a local path. Hugging Face Storage now joins that list as `store: hf`, reached through the `hf://` scheme:\n\n```yaml\nfile_mounts:\n  # A Hugging Face Bucket, read-write, for checkpoints, logs, processed data.\n  /checkpoints:\n    source: hf://buckets/my-org/qwen-sft\n    store: hf\n    mode: MOUNT # or COPY\n  # A model repo, mounted read-only.\n  /base-model:\n    source: hf://Qwen/Qwen3.5-4B\n    store: hf\n    mode: MOUNT\n  # A dataset repo, pinned to a revision, read-only.\n  /data:\n    source: hf://datasets/my-org/my-dataset@main\n    store: hf\n    mode: MOUNT\n```\n\nThat one `hf://` scheme covers the whole lifecycle: read the **model** and **dataset** from their repos, write **checkpoints** to a Bucket while you train, publish the finished model back to a repo, and pull it onto inference servers when you serve. Most teams already keep their models and datasets on the Hub, so there is no migration step and no new storage account to create.\n\n`MOUNT` uses Hugging Face's [`hf-mount`](https://github.com/huggingface/hf-mount) FUSE backend, so a bucket or repo shows up as a local path next to SkyPilot's other FUSE mounts (`gcsfuse`, `blobfuse2`, `rclone`, `goofys`). The fetching happens at the filesystem layer: when your code issues a `read()`, the driver pulls just those bytes from the Xet backend, so only the data you actually touch crosses the network, and `hf-mount` keeps an on-disk cache so repeat reads stay local. That on-disk cache is the behavior SkyPilot gives its other backends under [`MOUNT_CACHED`](https://docs.skypilot.co/en/latest/reference/storage.html), where a plain `MOUNT` instead streams every read from the bucket with nothing kept locally. For the `hf` store, `MOUNT` and `MOUNT_CACHED` behave the same, so either mode keeps the cache.\n\nBecause reads are lazy, a process can start working through a large file before the whole file has downloaded, instead of blocking on a full copy first. That keeps the GPU busy almost immediately, training on data as it streams in rather than sitting idle (and billing) while a dataset or checkpoint copies down. It pays off most on the first epoch, when nothing is cached yet. `COPY` takes the other route and downloads through `huggingface_hub` up front, with no special requirements.\n\nAuthentication is the token you already have. Set `HF_TOKEN` in your environment and hand it to a run with [`--secret HF_TOKEN`](https://docs.skypilot.co/en/latest/running-jobs/environment-variables.html); SkyPilot uses it for the mount on whatever cloud the job lands. One token works whether the job lands on AWS, GCP, Azure, Nebius, Lambda, or your own Kubernetes cluster, so there are no per-cloud bucket keys to juggle.\n\n## No egress: storage stops deciding where you run\n\nGPU capacity rarely comes from one place anymore. To get enough H100s and H200s, teams hold reserved and committed capacity across several vendors at once (a block on a hyperscaler, a cluster on a neocloud, maybe an on-prem rack) and run wherever they have allocation. SkyPilot is built for this: one job spec, scheduled across 20+ clouds, Kubernetes, and on-prem, landing on whichever reserved cluster is free.\n\nObject storage has been the catch. Object stores are regional and per-cloud, so feeding a GPU or an inference server that sits in a different vendor's data center means either keeping a copy of your data in every vendor's bucket or paying to pull it across. Most clouds charge egress (around $0.09/GB out of AWS) the moment data leaves their network, and often between regions inside one cloud. Pulling a base model onto every inference node, or iterating a dataset for several epochs from a cluster on another cloud, adds a hefty bill on top of GPUs you have already reserved. Teams end up pinning each run to whichever vendor holds the data and leaving the rest of their capacity idle.\n\nHugging Face Storage takes that cost off the table where it bites: the read side. With [no egress or CDN fees](https://huggingface.co/pricing) and storage at $12-18/TB/month (versus AWS S3 at roughly $23/TB plus egress), the same bucket is reachable from every one of those clusters, and reading from it is free no matter where the GPUs run. Writing back still costs your compute cloud's usual egress, the same as it would to any off-cloud store, but for most AI work the reads dominate: a dataset streamed over many epochs, or model weights pulled onto every new training or inference node. So you stop pinning each run to whichever vendor holds a copy of the data.\n\n## A quick benchmark\n\nTo collect some benchmark numbers, we ran a small fine-tune: [`Qwen/Qwen3.5-4B`](https://huggingface.co/Qwen/Qwen3.5-4B) on the [`HuggingFaceH4/Multilingual-Thinking`](https://huggingface.co/datasets/HuggingFaceH4/Multilingual-Thinking) dataset with TRL's [`SFTTrainer`](https://huggingface.co/docs/trl/sft_trainer), mounting the model read-only from its Hub repo and writing every checkpoint to a Hugging Face Bucket. The same SkyPilot YAML ran on AWS, GCP, and Lambda, changing only `--infra`. SkyPilot placed each job wherever GPUs were free, and all three read and wrote the same bucket.\n\n```yaml\n# qwen-sft.yaml. Launch anywhere: sky launch qwen-sft.yaml --infra aws|gcp|...\nresources:\n  accelerators: H100:1 # or whatever the cloud has\n\nfile_mounts:\n  /base-model:\n    source: hf://Qwen/Qwen3.5-4B # read-only, lazy-mounted from the Hub\n    store: hf\n    mode: MOUNT\n  /checkpoints:\n    source: hf://buckets/my-org/qwen-sft # read-write Bucket\n    store: hf\n    mode: MOUNT\n\nrun: |\n  python train.py --model /base-model --output_dir /checkpoints\n```\n\nWhat we measured:\n\n- **The model loaded free on every cloud.** Lazy reads pull only what `from_pretrained` touches, so it was ready to train in about 30 seconds (up to ~500 MB/s). Because Hugging Face charges no egress, that pull cost nothing; had the model lived in S3, every read to a GPU on another cloud would have been billed egress (~$0.09/GB on AWS).\n- **Checkpoints streamed straight to the bucket** at up to ~170 MB/s (8.43 GB of weights each) and persisted past the GPU instance.\n\nPer cloud, checkpoints wrote to the bucket at:\n\n| Cloud              | GPU  | Checkpoint write |\n| ------------------ | ---- | ---------------- |\n| AWS (us-east-2)    | L40S | \\~168 MB/s       |\n| GCP (us-central1)  | L4   | \\~123 MB/s       |\n| Lambda (us-west-3) | H100 | \\~112 MB/s       |\n\n## Xet-backed storage: dedup for checkpoints and model variants\n\nHugging Face Buckets are built on [Xet](https://huggingface.co/docs/hub/xet/overview), which uses [content-defined chunking](https://huggingface.co/docs/hub/xet/deduplication) to split files into ~64 KB chunks and store each unique chunk once. Because the boundaries follow the content, an edit changes only the chunks it touches and the rest are recognized as already stored. This pays off in a few places:\n\n- **Incremental and adapter checkpoints.** When you freeze layers, train adapters, or otherwise leave most weights untouched between saves, only the changed chunks upload instead of the whole checkpoint.\n- **Model variants that share a base.** Fine-tunes and quantizations of one base model overlap heavily, so the shared chunks are stored once across all of them.\n- **Datasets you append to.** Logs like conversation traces or inference outputs grow by appending rows to large Parquet files. The existing row groups stay byte-identical, so only the new rows transfer: in Hugging Face's [test](https://huggingface.co/blog/parquet-cdc), appending 10K rows to a 100K-row table moved about 10 MB instead of the full ~106 MB. (If you edit or delete rows in place, write with `use_content_defined_chunking=True` to keep changes local.)\n- **Re-uploads skip what's already stored.** In our test, re-uploading an 8.43 GB blob already in the bucket took about 8 seconds, versus 24 seconds for the first upload, because only chunk hashes move. The same mechanism lets server-side `hf buckets cp` between repos and buckets copy by reference instead of re-uploading bytes.\n\nHow much you save depends on how much your artifacts overlap, but the deduplication is automatic: you write a checkpoint as usual, and only the new chunks leave the machine.\n\n## Get started\n\n```bash\npip install \"skypilot[huggingface]\"\nhf auth login  # or: export HF_TOKEN=<your-token>\n```\n\nAdd an `hf://` mount to any SkyPilot task and launch. `MOUNT` needs a base image with glibc 2.34+ and `/dev/fuse`.\n\n## Built together: Hugging Face and SkyPilot\n\nThe initial `store: hf` support [started as a contribution](https://github.com/skypilot-org/skypilot/pull/9418) from Nikhil Jha. The Hugging Face team [carried it forward](https://github.com/skypilot-org/skypilot/pull/9698) and upstreamed the `hf-mount` FUSE fixes that let it mount in unprivileged containers, the default on many Kubernetes clusters. The SkyPilot team wired it into the storage backend. The whole path is open source: SkyPilot, Hugging Face's `hf-mount`, and the `huggingface_hub` client.\n\n## Resources\n\n- [SkyPilot storage docs](https://docs.skypilot.co/en/latest/reference/storage.html)\n- [Hugging Face Storage Buckets guide](https://huggingface.co/docs/hub/storage-buckets)\n- [`hf-mount`](https://github.com/huggingface/hf-mount)\n- [Xet: content-defined chunking and deduplication](https://huggingface.co/docs/hub/xet/deduplication)\n- [SkyPilot Slack community](https://slack.skypilot.co/)\n",
  "dateCreated": "2026-10-19T01:26:53.998049939+00:00",
  "date_created": "2026-07-07T00:00:00+00:00",
  "date_modified": "2026-07-07T00:00:00+00:00",
  "date_published": "2026-07-07T00:00:00+00:00",
  "description": "For most teams, models and datasets live in a bucket in one region of one cloud. The GPUs you can get, whether for development, training, or serving, increasingly sit on a different cloud than your data. The moment those two come apart,…",
  "external_url": "https://huggingface.co/blog/skypilot-hf-storage",
  "git": {
    "commit": "ebb0941c5c06a2ab2377342d607535262a3e969e",
    "path": "items/huggingface-co/2026/07/2026-07-07-run-ai-workloads-on-any-cloud-store-on-hugging-face-zero.md",
    "permalink": "https://github.com/aymericbeaumet/aggr-instance/blob/ebb0941c5c06a2ab2377342d607535262a3e969e/items/huggingface-co/2026/07/2026-07-07-run-ai-workloads-on-any-cloud-store-on-hugging-face-zero.md"
  },
  "id": "urn:aggr:item:1415c879ceff0545d57e2fcc0d75d17809c03ee7",
  "isBasedOn": {
    "@id": "https://huggingface.co/blog/skypilot-hf-storage"
  },
  "mainEntity": {
    "@id": "https://huggingface.co/blog/skypilot-hf-storage",
    "@type": "CreativeWork",
    "archivedAt": {
      "@id": "https://aggr.aymericbeaumet.com/items/huggingface-co/2026-07-07-run-ai-workloads-on-any-cloud-store-on-hugging-face-zero/#webpage"
    },
    "dateModified": "2026-07-07T00:00:00+00:00",
    "datePublished": "2026-07-07T00:00:00+00:00",
    "headline": "Run AI workloads on any cloud, store on Hugging Face: zero-egress storage with SkyPilot",
    "image": {
      "@type": "ImageObject",
      "caption": null,
      "height": 90,
      "url": "https://huggingface.co/blog/assets/skypilot-hf-storage/thumbnail.png",
      "width": 160
    },
    "timeRequired": "PT8M",
    "url": "https://huggingface.co/blog/skypilot-hf-storage",
    "wordCount": 1729
  },
  "name": "Archived snapshot: Run AI workloads on any cloud, store on Hugging Face: zero-egress storage with SkyPilot",
  "preview": {
    "alt": null,
    "color": null,
    "height": 90,
    "placeholder": {
      "data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=",
      "hash": ""
    },
    "url": "https://huggingface.co/blog/assets/skypilot-hf-storage/thumbnail.png",
    "width": 160
  },
  "reading_minutes": 8,
  "replicated_at": null,
  "source": "huggingface-co",
  "source_name": "Hugging Face - Blog",
  "summary": null,
  "tags": [],
  "temporalCoverage": "2026-10-19T01:26:53.998049939+00:00",
  "title": "Run AI workloads on any cloud, store on Hugging Face: zero-egress storage with SkyPilot",
  "url": "https://aggr.aymericbeaumet.com/items/huggingface-co/2026-07-07-run-ai-workloads-on-any-cloud-store-on-hugging-face-zero/",
  "word_count": 1729
}