Danh sách bài viết

Bài 12: Deploy demo lên Hugging Face Spaces

Đưa app Gradio hoặc Streamlit lên Hugging Face Spaces để có URL public dùng được lâu dài: cấu trúc Space, README.md frontmatter, push code qua git, quản lý secrets, persistent storage workaround, hardware tiers và những giới hạn cần biết trước khi deploy.

27/05/2026
0 lượt xem
1

Mục tiêu bài học

Sau bài này bạn sẽ:

  • ✅ Hiểu HF Spaces hoạt động như thế nào (git repo + hosted runner)
  • ✅ Tạo Space mới và chọn đúng SDK cho app
  • ✅ Viết đúng README.md frontmatter để Space build thành công
  • ✅ Push code bằng git (bao gồm xử lý file lớn với git-lfs)
  • ✅ Quản lý secrets — không hard-code API key vào code
  • ✅ Biết giới hạn của free tier và khi nào cần upgrade hardware
2

Hugging Face Spaces là gì

Hugging Face Spaces là platform hosting demo ML. Mỗi Space là một git repository — push code lên là HF tự build và chạy app, không cần cấu hình server.

Spaces hỗ trợ 4 SDK:

  • Gradio — chạy app.py với demo.launch()
  • Streamlit — chạy app.py với streamlit run
  • Docker — bạn tự viết Dockerfile, HF build và chạy container
  • Static — hosting HTML/CSS/JS thuần, không có server-side Python

URL của một Space có dạng:

https://huggingface.co/spaces/<username>/<space-name>

App chạy ở cùng URL đó (không phải subdomain riêng).

Free tier

CPU Basic (miễn phí):

  • 2 vCPU
  • 16 GB RAM
  • Space phải public (private cần Pro plan $9/tháng)
  • App tự sleep sau 48 giờ không có request — lần truy cập tiếp theo phải cold start (~30-60 giây)

Free tier đủ dùng cho demo model nhỏ (embedding, classification, text generation với model ≤ 7B đã quantize). Nếu app cần GPU, phải upgrade hardware (trả tiền theo giờ).

3

Tạo Space mới

  1. Đăng nhập tại https://huggingface.co/login.
  2. Nhấn nút New Space (góc phải trên, hoặc từ profile menu).
  3. Điền thông tin:
    • Owner: username hoặc organization
    • Space name: slug dùng trong URL, chỉ chứa chữ cái, số, dấu gạch ngang
    • SDK: chọn Gradio hoặc Streamlit tùy app bạn đã build ở bài 9-10
    • SDK version: khuyến nghị chọn version cụ thể khớp với local (tránh breaking change)
    • Hardware: CPU basic (free)
    • License: apache-2.0, mit, hoặc other
    • Visibility: Public (free) hoặc Private (Pro)
  4. Nhấn Create Space.

Sau khi tạo, HF tạo git repo rỗng với README.md mẫu. Bước tiếp theo là đẩy code lên.

4

Cấu trúc file trong Space

Cấu trúc tối thiểu cho Gradio hoặc Streamlit Space:

my-space/
├── app.py              # Entry point bắt buộc
├── requirements.txt    # Dependencies — HF tự pip install
└── README.md           # Bắt buộc có YAML frontmatter

Nếu có thêm module phụ hoặc assets:

my-space/
├── app.py
├── utils.py
├── requirements.txt
├── README.md
├── .gitignore
└── assets/
    └── sample.jpg      # File ví dụ, ảnh mẫu, ...

README.md — YAML frontmatter

HF đọc metadata từ YAML block ở đầu README.md. Nếu thiếu hoặc sai field, build sẽ fail:

---
title: My Gradio App
emoji: 🤖
colorFrom: blue
colorTo: purple
sdk: gradio
sdk_version: "4.44.0"
app_file: app.py
pinned: false
license: apache-2.0
---

# My Gradio App

Mô tả ngắn về Space này...

Các field quan trọng:

  • sdk: gradio, streamlit, docker, hoặc static
  • sdk_version: version cụ thể, phải là string (để trong dấu ngoặc kép). Nếu bỏ trống, HF dùng version mới nhất — có thể gây breaking change khi Gradio hoặc Streamlit release major version mới.
  • app_file: đường dẫn tới entry point, mặc định là app.py
  • pinned: true để Space luôn hiển thị ở đầu profile

Với Streamlit, frontmatter giống hệt, chỉ đổi sdk: streamlitsdk_version tương ứng (ví dụ "1.36.0").

5

Push code — web UI và git

Cách 1: Upload qua web UI

Vào trang Space → tab Files → nút Add file → drag-drop hoặc chọn file. Phù hợp khi chỉ có vài file nhỏ hoặc cần sửa nhanh một file cụ thể.

Cách 2: Git (khuyến nghị)

Space là git repo, push code như mọi git repo khác. Mỗi lần push → HF tự trigger build:

# Cài git-lfs nếu chưa có (cần cho file > 10MB)
git lfs install

# Clone repo của Space về local
git clone https://huggingface.co/spaces/<username>/<space-name>
cd <space-name>

# Thêm/sửa file
cp /path/to/your/app.py .
cp /path/to/your/requirements.txt .
# Sửa README.md frontmatter cho đúng

git add app.py requirements.txt README.md
git commit -m "add initial app"
git push

Nếu cần xác thực khi push, có hai cách:

  • HTTPS + token: dùng HF access token thay mật khẩu (tạo token tại https://huggingface.co/settings/tokens)
  • SSH: thêm SSH key tại https://huggingface.co/settings/keys, rồi clone qua git clone [email protected]:spaces/<username>/<space-name>

File lớn và git-lfs

HF Spaces dùng git-lfs để lưu file > 10MB (model weights, dataset, ảnh lớn). File không theo dõi bởi lfs sẽ bị từ chối nếu vượt giới hạn 10MB của git thông thường:

# Track file theo extension
git lfs track "*.bin"
git lfs track "*.pt"
git lfs track "*.safetensors"

# .gitattributes sẽ được tạo/cập nhật tự động
git add .gitattributes
git add model.bin
git commit -m "add model weights"
git push

Model lớn (> 1-2GB) không nên lưu trong Space repo. Thay vào đó, load runtime từ HF Hub bằng from_pretrained() — xem mục 11.

6

Build và run lifecycle

Mỗi lần push code, HF chạy build pipeline theo thứ tự:

  1. Detect SDK: đọc sdk field trong README.md frontmatter
  2. Install dependencies: chạy pip install -r requirements.txt
  3. Start app: với Gradio → python app.py; với Streamlit → streamlit run app.py --server.port 7860 --server.address 0.0.0.0
  4. Reverse proxy: HF expose app ra HTTPS — port 7860 (Gradio) hoặc 8501 (Streamlit) bên trong container được map tới URL public

Để xem log build và runtime, vào tab Logs trong trang Space. Log cập nhật real-time. Nếu build fail, lỗi thường ở bước install dependency (package không tồn tại, conflict version) hoặc import error khi start app.

Build timeout

HF giới hạn build tối đa 1 giờ. Nếu cài dependency mất quá lâu (pytorch với CUDA index, các package C++ extension lớn), build sẽ bị kill. Giải pháp: dùng Docker SDK để tự kiểm soát build environment, hoặc tối giản requirements.txt (dùng CPU-only torch nếu không cần GPU).

CPU-only PyTorch

Cài PyTorch đầy đủ CUDA trên CPU-only Space sẽ download ~2GB, dễ timeout. Dùng CPU wheel riêng:

# requirements.txt — cài torch CPU-only để build nhanh hơn
--extra-index-url https://download.pytorch.org/whl/cpu
torch==2.3.0+cpu
torchvision==0.18.0+cpu
7

Ví dụ đầy đủ — Gradio image classifier

Ví dụ này deploy một image classifier ResNet-50 lên HF Spaces. Gồm 3 file.

app.py

import gradio as gr
from PIL import Image
import torch
from torchvision import models, transforms
import json
import urllib.request

# ---- Load class labels ----
LABELS_URL = (
    "https://raw.githubusercontent.com/anishathalye/imagenet-simple-labels"
    "/master/imagenet-simple-labels.json"
)
with urllib.request.urlopen(LABELS_URL) as r:
    IMAGENET_LABELS: list[str] = json.loads(r.read())

# ---- Load model (1 lần khi container khởi động) ----
model = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)
model.eval()

preprocess = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225],
    ),
])

def classify(img: Image.Image) -> dict[str, float]:
    tensor = preprocess(img).unsqueeze(0)
    with torch.no_grad():
        logits = model(tensor)
    probs = torch.softmax(logits[0], dim=0)
    top5 = probs.topk(5)
    return {
        IMAGENET_LABELS[idx.item()]: round(prob.item(), 4)
        for prob, idx in zip(top5.values, top5.indices)
    }

demo = gr.Interface(
    fn=classify,
    inputs=gr.Image(type="pil"),
    outputs=gr.Label(num_top_classes=5),
    title="ResNet-50 Image Classifier",
    description="Upload ảnh để xem top-5 predictions.",
)

# HF Spaces chạy app.py trực tiếp, không cần if __name__ == "__main__"
# nhưng để tương thích local thì thêm vào cũng được
demo.launch()

requirements.txt

--extra-index-url https://download.pytorch.org/whl/cpu
gradio==4.44.0
torch==2.3.0+cpu
torchvision==0.18.0+cpu
pillow

README.md

---
title: ResNet50 Image Classifier
emoji: 🖼️
colorFrom: green
colorTo: blue
sdk: gradio
sdk_version: "4.44.0"
app_file: app.py
pinned: false
license: apache-2.0
---

# ResNet-50 Image Classifier

Demo phân loại ảnh với ResNet-50 pretrained trên ImageNet.
Model chạy trên CPU — inference mỗi ảnh ~1-2 giây.

Push và kiểm tra

git clone https://huggingface.co/spaces/<your-username>/resnet50-demo
cd resnet50-demo

# Copy 3 file vào thư mục
git add app.py requirements.txt README.md
git commit -m "init: resnet50 image classifier"
git push

Sau khi push, vào tab Logs của Space để theo dõi. Build thường mất 2-5 phút (download torch CPU wheel ~200MB, cài gradio). Khi log in dòng cuối Running on public URL hoặc You can now view your Streamlit app, app đã online.

8

Secrets và environment variables

Space repo là public (ở free tier) — bất kỳ ai cũng xem được code. Không được hard-code API key, token hay credential vào app.py.

HF Spaces cung cấp hai loại biến môi trường, cấu hình tại Settings → Variables and secrets:

Loại Hiển thị trong Settings Log khi build/run Dùng cho
Variable Có (public) Có thể xuất hiện Config không nhạy cảm: MODEL_NAME, MAX_TOKENS, DEBUG
Secret Không (ẩn sau khi save) Không log API key, token: OPENAI_API_KEY, HF_TOKEN

Đọc trong code

Cả Variable và Secret đều đọc qua os.environ:

import os
from openai import OpenAI

# Secret: không hard-code, đọc từ env
api_key = os.environ.get("OPENAI_API_KEY")
if not api_key:
    raise ValueError("OPENAI_API_KEY chưa được set trong Space Secrets")

client = OpenAI(api_key=api_key)

HF_TOKEN — download model private

Khi dùng from_pretrained() để load model từ HF Hub mà model đó private hoặc gated (yêu cầu accept license), cần set HF_TOKEN trong Secrets:

import os
from transformers import AutoTokenizer, AutoModelForCausalLM

# HF Spaces tự đọc HF_TOKEN từ env nếu set trong Secrets
# transformers >= 4.x tự dùng token này khi download
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-chat-hf")
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-chat-hf")

Nếu không set HF_TOKEN, download model từ gated repo sẽ fail với 401 Unauthorized hoặc bị rate limit khi anonymous.

9

Persistent storage

Mặc định, filesystem của Space là ephemeral — file ghi trong lúc runtime (ví dụ log user, kết quả đã cache) bị mất khi Space restart hoặc sau sleep.

Persistent storage (paid)

HF Spaces có tính năng Persistent Storage (mount disk) dạng trả phí, cấu hình tại Settings → Persistent storage. Disk được mount vào /data bên trong container.

Workaround miễn phí: HF Datasets làm storage

Nếu cần lưu data giữa các session mà không muốn trả phí persistent disk, dùng HF Datasets repo như một "database đơn giản" qua thư viện huggingface_hub:

import os
from huggingface_hub import CommitOperationAdd, HfApi

api = HfApi(token=os.environ["HF_TOKEN"])

def save_feedback(user_input: str, model_output: str, rating: int):
    """Ghi feedback vào HF Dataset repo."""
    # Append vào file CSV
    content = f'"{user_input}","{model_output}",{rating}\n'
    api.upload_file(
        path_or_fileobj=content.encode(),
        path_in_repo="feedback.csv",
        repo_id="<username>/my-feedback-dataset",
        repo_type="dataset",
        commit_message="add feedback",
    )

Cách này có giới hạn: write operation chậm (~1-2s mỗi lần commit), không phù hợp ghi nhiều request đồng thời. Chỉ dùng cho feedback form, logging không critical.

10

Hardware tiers

Upgrade hardware tại Settings → Hardware. Tính tiền theo giờ thực sự sử dụng (không tính giờ Space đang sleep).

Tier Specs Giá Dùng cho
CPU Basic 2 vCPU, 16 GB RAM Miễn phí Demo nhỏ, model đã quantize chạy CPU
CPU Upgrade 8 vCPU, 32 GB RAM ~$0.03/h App cần nhiều CPU hơn, inference song song
T4 Small T4 GPU 16 GB VRAM, 4 vCPU, 15 GB RAM ~$0.40/h SLM 7B với 4-bit quantization (llama.cpp, bitsandbytes)
T4 Medium T4 GPU 16 GB VRAM, 8 vCPU, 30 GB RAM ~$0.60/h Model 13B quantize hoặc pipeline inference nặng
A10G Small A10G 24 GB VRAM, 4 vCPU, 15 GB RAM ~$1.05/h Model 13-30B, image generation (SD)
A100 Large A100 80 GB VRAM, 12 vCPU, 142 GB RAM ~$4.13/h Fine-tuning nhỏ, model 70B

Giá trên tính đến Q2 2025, có thể thay đổi. Kiểm tra trang https://huggingface.co/pricing trước khi dùng.

Sleep when inactive

Free CPU Space tự sleep sau 48 giờ không có request. Space paid cũng có thể cấu hình sleep để tiết kiệm chi phí — vào Settings → Sleep time. Khi Space sleep, billing dừng; khi có request mới, Space wake up (cold start ~30-60 giây trên CPU, lâu hơn trên GPU).

11

Limits và gotchas

1. Build timeout 1 giờ

Nếu pip install mất hơn 1 giờ (thường do pytorch + CUDA), build bị kill. Giải pháp:

  • Dùng CPU-only torch wheel (xem mục 6)
  • Chuyển sang Docker SDK để pre-build image với layer cache
  • Chỉ cài package thực sự cần — tránh cài cả transformers[all] khi chỉ cần base

2. Model lớn trong repo → chậm và tốn quota

Không lưu model weights (file .bin, .safetensors hàng GB) trong Space repo. Load runtime từ HF Hub thay vào đó:

from transformers import pipeline

# Load từ HF Hub, tự cache trong /root/.cache/huggingface
# Lần đầu chạy sẽ download, lần sau dùng cache
pipe = pipeline(
    "text-classification",
    model="distilbert-base-uncased-finetuned-sst-2-english",
)

Nhược điểm: cold start lần đầu phải download, mất thêm thời gian. Với model lớn và Space free, mỗi lần restart đều download lại (cache ephemeral).

3. Anonymous download bị rate limit

Khi Space tải model mà không có HF_TOKEN, HF tính là anonymous request và rate limit chặt hơn. Set HF_TOKEN trong Secrets dù model là public — authenticated request có rate limit cao hơn.

4. Port cố định

HF Spaces chỉ expose port 7860. Không thể bind app ra port khác. Gradio mặc định đúng port này. Streamlit cần cấu hình tường minh:

# Không cần, HF tự truyền args khi start Streamlit
# Nhưng nếu chạy local cần port khác thì:
# streamlit run app.py --server.port 7860

Với Docker SDK, phải EXPOSE 7860 và bind app vào port 7860.

5. Không có outbound internet restrictions (nhưng có giới hạn tốc độ)

Space có thể gọi external API (OpenAI, Anthropic, ...). Tốc độ network outbound trên CPU Basic tương đối chậm — nếu app cần download model lớn khi runtime, dùng GPU tier sẽ có bandwidth tốt hơn.

6. Docker image size

Với Docker SDK, nếu image sau build vượt khoảng 10-15 GB, build có thể fail hoặc rất chậm. Dùng multi-stage build và chỉ copy artifacts cần thiết vào final stage.

12

Alternatives

Ngoài HF Spaces, có ba lựa chọn phổ biến khác:

  • Streamlit Community Cloud (streamlit.io/cloud) — miễn phí cho app Streamlit, link trực tiếp GitHub repo. Chỉ hỗ trợ Streamlit, không hỗ trợ Gradio hay Docker.
  • Modal Labs (modal.com) — serverless GPU, phù hợp cho workload inference nặng hoặc app cần scale linh hoạt hơn. Cần viết decorator @app.function và deploy khác với Gradio/Streamlit thông thường.
  • Render / Railway — general-purpose hosting, hỗ trợ mọi loại Python app. Phù hợp khi muốn deploy FastAPI + Gradio cùng một service (bài 36-37 sẽ đề cập chi tiết).

HF Spaces ưu điểm rõ ở điểm không cần cấu hình infrastructure, phù hợp nhất khi app là pure demo ML và không cần custom domain hay scale phức tạp.

13

Tóm tắt

  • ✅ Space = git repo, push code = deploy — không cần cấu hình server
  • README.md YAML frontmatter bắt buộc: sdk, sdk_version, app_file
  • ✅ File > 10MB dùng git-lfs; model lớn load runtime từ HF Hub thay vì lưu trong repo
  • ✅ API key và token — set trong Secrets, đọc qua os.environ, không hard-code
  • HF_TOKEN trong Secrets để tránh rate limit khi download model từ Hub
  • ✅ Free tier: CPU 2 vCPU / 16 GB RAM, sleep sau 48h không request; T4 Small ($0.40/h) đủ cho SLM 7B 4-bit
  • ✅ Build timeout 1 giờ — dùng CPU-only torch wheel để build nhanh hơn
  • ✅ Persistent storage mặc định ephemeral; workaround miễn phí là dùng HF Datasets repo