# GitHub → Hugging Face Space with Custom Domain — Setup Guide A complete, repeatable playbook. Follow in order. Sections marked ⚠️ are the common pitfall spots. --- ## Table of Contents 1. [Prerequisites](#1-prerequisites) 2. [Create the Hugging Face Space](#2-create-the-hugging-face-space) 3. [Configure the GitHub Repository](#3-configure-the-github-repository) 4. [Set Up the Sync Workflow](#4-set-up-the-sync-workflow) 5. [README Front Matter (Critical)](#5-readme-front-matter-critical) 6. [Custom Domain Setup](#6-custom-domain-setup) 7. [Gradio `root_path` Rule](#7-gradio-root_path-rule) 8. [Verify End-to-End](#8-verify-end-to-end) 9. [Troubleshooting Reference](#9-troubleshooting-reference) --- ## 1. Prerequisites | What | Where to get it | |---|---| | Hugging Face account | https://huggingface.co | | HF write token (fine-grained or classic) | HF → Settings → Access Tokens → **New token** → Role: **Write** | | GitHub repo (public or private) | github.com | | Domain registrar access (for custom domain) | Your registrar (Cloudflare, Namecheap, etc.) | --- ## 2. Create the Hugging Face Space 1. Go to https://huggingface.co/new-space 2. Fill in: - **Owner**: your HF username or org - **Space name**: e.g. `github-sync-test` - **SDK**: Gradio (or Streamlit) - **Visibility**: Public or Private 3. Click **Create Space** — the Space is created with a default `README.md`. > You do **not** need to push any code manually; the GitHub Action will do it. --- ## 3. Configure the GitHub Repository ### 3.1 Add the HF token as a GitHub Secret 1. GitHub repo → **Settings** → **Secrets and variables** → **Actions** 2. Click **New repository secret** - Name: `HF_TOKEN` - Value: your HF write token from step 1 3. Save. ### 3.2 Minimum required files ``` your-repo/ ├── README.md ← MUST have HF front matter (see §5) ├── main.py ← your Gradio app entry point ├── requirements.txt ← at minimum: gradio>=5.0 └── .github/ └── workflows/ └── sync.yml ``` --- ## 4. Set Up the Sync Workflow Create `.github/workflows/sync.yml`: ```yaml name: Sync to Hugging Face on: push: branches: [master] # or "main" — match your default branch workflow_dispatch: # allows manual trigger from GitHub UI jobs: sync: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # full history required by hub-sync lfs: true - name: Write VERSION file run: echo "${{ github.sha }}" > VERSION - uses: huggingface/hub-sync@main with: github_repo_id: YOUR_GH_USERNAME/YOUR_GH_REPO huggingface_repo_id: YOUR_HF_USERNAME/YOUR_SPACE_NAME repo_type: space # MUST be "space" for HF Spaces hf_token: ${{ secrets.HF_TOKEN }} ``` **Replace** `YOUR_GH_USERNAME/YOUR_GH_REPO` and `YOUR_HF_USERNAME/YOUR_SPACE_NAME` with your actual values. --- ## 5. README Front Matter (Critical) ⚠️ **The single most common cause of sync failures.** The `README.md` at the repo root **must** start with this YAML front matter block. Without it, HF rejects the push with "Missing configuration in README". ```markdown --- title: your-space-name emoji: "🤗" colorFrom: blue colorTo: indigo sdk: gradio app_file: main.py pinned: false --- # Your Space Title ...rest of your README... ``` | Field | Notes | |---|---| | `title` | Must match (or at least not conflict with) your HF Space name | | `sdk` | `gradio` or `streamlit` | | `app_file` | The Python file that launches your app (e.g. `main.py`) | | `emoji`, `colorFrom`, `colorTo` | Cosmetic — any valid values work | | `pinned` | `true` pins the Space on your profile | --- ## 6. Custom Domain Setup ### 6.1 Add the domain in HF Space settings 1. HF Space → **Settings** → **Custom domains** 2. Enter your domain: e.g. `demo.example.com` 3. HF shows you a **CNAME target** (looks like `billyaungmyint-github-sync-test.hf.space`) 4. Copy that CNAME target value. ### 6.2 Add the DNS record at your registrar | Type | Host | Value | TTL | |---|---|---|---| | `CNAME` | `demo` (subdomain part only) | `billyaungmyint-github-sync-test.hf.space` | 300 or Auto | > If you want an apex/root domain (`example.com`), use a **CNAME Flattening** or **ALIAS** record — check your registrar's docs. Most registrars support this via Cloudflare's "CNAME at root" feature. ### 6.3 Wait for DNS propagation - Typical: 1–5 minutes on Cloudflare, up to 48 hours on others. - Verify with: `nslookup demo.example.com` — should resolve to HF's servers. - HF will automatically provision a TLS certificate once the CNAME resolves. ### 6.4 ⚠️ No `GRADIO_ROOT_PATH` needed When using a custom domain at the **root path** (e.g. `https://demo.example.com`): - Do **not** set `GRADIO_ROOT_PATH` in HF Space variables. - Leave it unset (or set to `""`). - Setting it to the old `.hf.space` URL **after** a domain change causes `"Could not get API info. fetch failed"`. Only set `GRADIO_ROOT_PATH` if your app is served at a **subpath**, e.g. `https://example.com/myapp` → set `GRADIO_ROOT_PATH=/myapp`. --- ## 7. Gradio `root_path` Rule Use this pattern in your `main.py` — it is safe for all scenarios: ```python import os if __name__ == "__main__": # Strip trailing slash to avoid double-slash issues. # Leave GRADIO_ROOT_PATH unset (or "") for custom domains at root. _root_path = os.getenv("GRADIO_ROOT_PATH", "").rstrip("/") demo.launch(server_name="0.0.0.0", root_path=_root_path) ``` | Deployment scenario | `GRADIO_ROOT_PATH` env var value | |---|---| | Default `.hf.space` URL | *(unset)* or `""` | | Custom domain at root (`myapp.com`) | *(unset)* or `""` | | Custom domain at subpath (`myapp.com/demo`) | `/demo` | | Local dev | *(unset)* — runs fine | --- ## 8. Verify End-to-End Run through this checklist after initial setup or after any DNS/domain change: - [ ] `nslookup your-domain.com` resolves (no NXDOMAIN) - [ ] `https://your-domain.com` loads the Space (no TLS error) - [ ] Browser DevTools → Network: no failed `/info` or `queue/join` requests - [ ] Push a trivial commit to GitHub → Action runs green → Space rebuilds → change appears - [ ] HF Space settings → Custom domains → Status shows ✅ (not "Pending") --- ## 9. Troubleshooting Reference | Error | Cause | Fix | |---|---|---| | `Missing configuration in README` | README has no HF YAML front matter | Add the front matter block (see §5) | | `Could not get API info. fetch failed` | `GRADIO_ROOT_PATH` set to wrong URL, or DNS not yet propagated | Clear `GRADIO_ROOT_PATH` in Space variables (see §6.4 & §7) | | `HF_TOKEN` secret not found / 401 | Secret missing or token lacks write scope | Re-add secret with a **Write** token (see §3.1) | | Action fails: `repo_type: space` error | Wrong `repo_type` in workflow | Set `repo_type: space` in `sync.yml` (see §4) | | Space stuck on "Building" | Dependency error in `requirements.txt` | Check Space build logs; pin a working version | | TLS certificate "Pending" for >30 min | CNAME not propagated yet or wrong target | Verify CNAME value matches exactly what HF shows | | Custom domain works but assets 404 | `root_path` set to a subpath that doesn't exist | Set `GRADIO_ROOT_PATH=""` or the correct subpath | | Space rebuilds but shows old code | `fetch-depth: 0` missing in workflow | Add `fetch-depth: 0` to `actions/checkout@v4` (see §4) |