TL;DR
A Hugging Face Space is a managed app-hosting tier built on a git repo. You push app.py, a requirements.txt and a README.md with a YAML block, and the platform builds a container and serves it at a URL. Three SDKs exist: gradio, docker and static. Gradio is the fastest path to a working demo.
Treat a Space as a demo and review environment, not production. Free CPU Spaces sleep on idle, share a multi-tenant box, carry no SLA, and run your code in a place you do not control. The moment a demo needs uptime, private data, access control or a stable address, move it to Inference Endpoints, a TGI container, or your own GPUs.
Someone on the data team sends you a link to a model demo and says it is broken. You open it and wait fifteen seconds while a grey screen says the Space is starting. Then it loads fine. Nothing is broken. The free Space went to sleep after a stretch of no traffic, and the first request had to wake it up. That single behavior tells you almost everything about what a Space is and is not. It is a convenient place to put a demo. It is not a place to put a service that someone expects to answer instantly, every time, with data you cannot afford to leak.
Spaces are the part of Hugging Face that finally makes a model visible to people who do not live in a terminal. That is genuinely useful. It is also where infra teams get into trouble, because a demo that works in a meeting quietly becomes the thing a department depends on, and nobody upgraded it to deserve that trust. This part covers how a Space runs, how to ship a Gradio one, and the line where a Space becomes the wrong tool.
What a Space is
Strip away the friendly framing and a Space is a managed platform-as-a-service tier sitting on top of a Hub git repository. The repo holds your code and config. When you push a commit, the platform reads the YAML block at the top of README.md, builds an image, installs your dependencies, starts a container, and exposes it behind a URL and an iframe. If you run an internal Heroku-style app tier or a managed container service, this is the same shape: git push in, running container out, with the build and the reverse proxy handled for you.
The config lives in the README.md YAML header, not in a separate manifest. Keys like sdk, sdk_version, app_file, app_port and python_version control the runtime. The default Python version is 3.10, and for a Docker Space the default exposed port is 7860. That is the whole control plane for a basic Space: a few lines of YAML and a file the platform knows how to run.
This is the same registry-plus-runtime idea you have seen before in this series. The Hub stores models the way a container registry stores images, a point covered in the Harbor for Beginners guide. A Space adds the part a registry does not: it runs the thing. That convenience is the whole appeal, and also the whole risk.
Three SDKs, and why Gradio wins for demos
The sdk key accepts three values: gradio, docker and static. Each is a different amount of control traded against a different amount of work. The old built-in Streamlit SDK is gone as a first-class option; if you want Streamlit now, you select the Docker SDK and use the Streamlit template. That change matters for planning, because it means anything beyond a Gradio or static app is a container you own end to end.
| SDK | What you write | Control | Best for |
|---|---|---|---|
| gradio | app.py plus requirements.txt | Low; platform owns the server | Model demos, quick UIs, internal review |
| docker | A Dockerfile and your full stack | High; you own the image and port | FastAPI, custom servers, Streamlit, special deps |
| static | HTML and JavaScript | None server-side | Front ends that call an API elsewhere |
Gradio wins for demos because it removes the server entirely. You write a Python function and describe its inputs and outputs, and Gradio builds the web UI, the request handling and the API for you. For an infra reader the trade is clear: you give up control of the web layer in exchange for not building one. That is the right trade for a demo and the wrong trade for a service you have to operate, which is exactly the line this part is about.
Shipping a Gradio Space, end to end
A working Gradio Space is three files. Here is the application file. It loads a small image-classification model through a transformers pipeline, the same pattern from earlier in this series, and wraps it in a Gradio interface.
# app.py
import gradio as gr
from transformers import pipeline
clf = pipeline(task="image-classification",
model="julien-c/hotdog-not-hotdog")
def predict(img):
preds = clf(img)
return {p["label"]: p["score"] for p in preds}
app = gr.Interface(
predict,
inputs=gr.Image(type="pil"),
outputs=gr.Label(num_top_classes=2),
title="Hot Dog or Not",
)
if __name__ == "__main__":
app.launch()The second file lists dependencies. The Spaces runtime installs these at build time.
# requirements.txt
transformers
torchThe third file is the YAML config at the top of README.md. This is what tells the platform it is a Gradio Space and which file to run.
# README.md (YAML header)
---
title: Hot Dog Or Not
emoji: 🥩
colorFrom: red
colorTo: yellow
sdk: gradio
app_file: app.py
pinned: false
---Worked example
Push those three files to a new Space repo. The build log shows pip installing transformers and torch, then the App tab renders an upload box. Drop in a photo and you get back two labels with scores, for example hot dog 0.98 and not hot dog 0.02. First load on free CPU takes several seconds because the model has to download and warm.
The common failure: the build finishes but the App tab shows a runtime error. Almost always it is a missing line in requirements.txt (the import works on your laptop because the package is already there, but the clean build box has nothing). Read the build and container logs in the Space, add the missing dependency, push again. The second build is the fix, not a restart.
Read the build log like a deploy
The part that trips up first-timers is treating a Space like a script instead of a deploy. It is a deploy. Every push triggers a build, the build has a log, and the running container has its own log, both visible in the Space UI. When something is wrong the answer is in one of those two places, the same way you would check a CI build log and then the container logs on any platform you run. Pin sdk_version in the YAML once a demo matters, because leaving it unset means a future build can pull a newer Gradio and change behavior under you, the classic floating-tag problem you already avoid with container images. Pin the version, read the logs, and a Space behaves like any other small deployment target rather than a black box.
Hardware, sleep, and the bill
Every Space starts on the free CPU Basic tier, which is a shared 2 vCPU box with 16 GB of RAM. That runs a small model demo fine. For anything that needs a GPU you upgrade the Space to a hardware flavor, and the platform swaps the underlying machine. The flavors run from a single T4 up through L4, L40S and A100 configurations, including multi-GPU options like a100x4 and l40sx8. There is also ZeroGPU, a free shared pool of H200-class GPUs that is allotted by quota rather than dedicated to your Space, which is meant for demos that need a GPU only in short bursts.
| Tier | Roughly what it is | Billing | Sleeps on idle |
|---|---|---|---|
| CPU Basic | 2 vCPU, 16 GB, shared | Free | Yes |
| ZeroGPU | Shared H200 pool, quota based | Free, metered by quota | Yes |
| GPU upgrade | T4 up to multi-A100 | Per hour while running | Optional |
Two behaviors decide whether a Space is safe to depend on. The first is sleep. Free tiers pause after a period of no traffic, and the next request pays the wake-up cost, which is the grey screen from the opening story. The second is the billing model on paid hardware: a GPU Space is charged for the time the hardware is assigned, so a Space you upgraded and forgot keeps billing whether anyone is using it or not. Neither behavior is a flaw. They are exactly what you would expect from a shared demo platform. They are also exactly why a Space is the wrong home for a service.
app.py or the repo, since the code is often public. Keep regulated or customer data off it, because the data leaves your boundary the moment a user uploads it. Set a budget alert on any GPU Space, and audit for upgraded Spaces nobody is watching, the cloud-bill equivalent of a forgotten always-on instance. If a Space starts getting real traffic, that is your signal to graduate it, not to upgrade it.When a Space is the wrong tool
The decision is not Space versus no Space. It is demo versus service, and a Space only belongs on one side of that line. The test I use is four questions. Does it need to answer instantly every time? Does it handle private or regulated data? Does it need access control beyond a public or simply-gated page? Does it need a stable address other systems depend on? One yes means a Space is the wrong tool.
Where to graduate to is a question this series already answers. For a managed but production-grade endpoint without running the box yourself, Inference Endpoints is the path, weighed against the other options in Part 11 on Inference Providers, Endpoints and self-hosting. For a serving container you scale and monitor yourself, Text Generation Inference is covered in Part 12 on TGI in production. For your own GPUs on the NVIDIA stack, cross over to the NVIDIA AI guide, and for an on-prem VMware build see the Private AI guide. A Space is the front porch. None of those are.
What I’d ship
Use Spaces, and reach for the Gradio SDK first. For showing a model to colleagues, gathering feedback, or putting a face on a proof of concept, nothing else gets you from a function to a shareable URL as fast, and the cost on CPU Basic is zero. I keep several Gradio Spaces around for exactly this, and I would recommend any infra team do the same. The recommendation has a hard boundary, though. Do not let a Space hold private data, do not let it become a load-bearing endpoint, and do not upgrade it to a GPU and call that production. The why-not is the same in every case: it is shared infrastructure with no SLA that sleeps on idle and bills by the hour. The thing to validate first is what your demo will carry, because the failure mode is not a Space breaking. It is a Space working well enough that someone forgets it was only ever a demo.
Try this: take one model you have been meaning to show people, wrap it in the three-file Gradio pattern above, and push it. Then write one sentence at the top of the README saying what it is not for. That sentence is the cheapest governance you will ever ship.
References
- Gradio Spaces (Hugging Face Hub docs)
- Spaces Configuration Reference (Hugging Face Hub docs)
- Spaces Overview (Hugging Face Hub docs)


DrJha