tao-shen commited on
Commit
8e80192
·
1 Parent(s): a3bfafd

HuggingRun: 通用接口 + HF 限制文档 + APP_PORT + ubuntu-desktop 示例

Browse files
.env.example CHANGED
@@ -16,5 +16,6 @@ RUN_CMD=
16
  # Sync interval in seconds
17
  SYNC_INTERVAL=60
18
 
19
- # Port for default demo (HF Spaces expect 7860)
20
  PORT=7860
 
 
16
  # Sync interval in seconds
17
  SYNC_INTERVAL=60
18
 
19
+ # Port your app listens on (HF exposes only this one port; default 7860)
20
  PORT=7860
21
+ APP_PORT=7860
DEPLOY.md ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 部署 HuggingRun 到 Hugging Face Spaces
2
+
3
+ ## 1. 创建 Space
4
+
5
+ 在 [huggingface.co/new-space](https://huggingface.co/new-space) 新建 Space:
6
+
7
+ - **SDK**: 选 **Docker**
8
+ - **Space name**: 例如 `HuggingRun`
9
+ - 创建后得到 `https://huggingface.co/spaces/<你的用户名>/HuggingRun`
10
+
11
+ ## 2. 推送代码
12
+
13
+ 在本地项目目录执行(把 `tao-shen` 换成你的 HF 用户名):
14
+
15
+ ```bash
16
+ cd /Users/tao.shen/HuggingRun
17
+ git remote remove origin 2>/dev/null || true
18
+ git remote add origin https://huggingface.co/spaces/<你的用户名>/HuggingRun
19
+ git push -u origin main
20
+ ```
21
+
22
+ 若 HF 需要认证,先执行:
23
+
24
+ ```bash
25
+ pip install -q huggingface_hub && huggingface-cli login
26
+ ```
27
+
28
+ 或使用 Git 凭证(HF 支持 Personal Access Token 作为密码)。
29
+
30
+ ## 3. 本地验证(可选)
31
+
32
+ 确保 Docker 已启动后:
33
+
34
+ ```bash
35
+ docker build -t huggingrun:local .
36
+ docker run --rm -p 7860:7860 -e PORT=7860 huggingrun:local
37
+ ```
38
+
39
+ 浏览器打开 http://localhost:7860 应看到默认 demo(访问计数)。Ctrl+C 停止。
40
+
41
+ ## 4. 难例测试(FastAPI + SQLite)
42
+
43
+ 在 Space 的 **Settings → Repository secrets** 中增加:
44
+
45
+ - `RUN_CMD` = `uvicorn app.fastapi_sqlite:app --host 0.0.0.0 --port 7860`
46
+ - (可选)`HF_TOKEN`、`AUTO_CREATE_DATASET` = `true` 以开启持久化
47
+
48
+ 保存后 Space 会重新构建。打开 App 查看首页和 `/api`,重启 Space 后访问次数应保持(若已配 HF 持久化)。
49
+
50
+ ## 完成标准
51
+
52
+ - [ ] Space 创建并推送代码成功
53
+ - [ ] 默认 demo 在 Space 中可访问(7860)
54
+ - [ ] 设置 `RUN_CMD` 后 FastAPI+SQLite 难例可访问且(可选)重启后数据仍在
DESIGN.md CHANGED
@@ -33,7 +33,8 @@
33
 
34
  ## 完成标准(迭代开发)
35
 
 
36
  - [ ] 本地 `docker build` 与 `docker run` 成功,默认 demo 在 7860 响应。
37
  - [ ] 持久化:重启容器后,写入 `PERSIST_PATH` 的内容仍在(通过 HF Dataset 恢复)。
38
- - [ ] 至少一个难(如 FastAPI+SQLite 或 Gradio+状态)在 README 中说明并在 HF 上可复现
39
  - [ ] 代码推送到 HF Space,且在该 Space 中成功运行并通过验证。
 
33
 
34
  ## 完成标准(迭代开发)
35
 
36
+ - [ ] **通用工具**:README 与 docs/HF_LIMITATIONS.md 明确“通用接口 + HF 限制应对”;entrypoint/sync 可配置 PERSIST_PATH、APP_PORT、RUN_CMD。
37
  - [ ] 本地 `docker build` 与 `docker run` 成功,默认 demo 在 7860 响应。
38
  - [ ] 持久化:重启容器后,写入 `PERSIST_PATH` 的内容仍在(通过 HF Dataset 恢复)。
39
+ - [ ] 最小化:FastAPI+SQLite、Ubuntu 桌面等仅演示“用通用工具”的用法,不增加额外通用逻辑
40
  - [ ] 代码推送到 HF Space,且在该 Space 中成功运行并通过验证。
README.md CHANGED
@@ -19,57 +19,64 @@ tags:
19
 
20
  **Run anything on Hugging Face.**
21
 
22
- Turn Hugging Face Spaces into a generic container: run any Docker-friendly app with optional persistence via HF Datasets. No Mac Mini, no paid cloud — 2 vCPU, 16GB RAM, 50GB storage, free tier.
23
 
24
- ## Why HuggingRun?
 
25
 
26
- - **Generic**: Not tied to one app. Set `RUN_CMD` to run your server, API, or bot.
27
- - **Persistent**: Sync a directory (e.g. `/data`) to a private HF Dataset so state survives restarts.
28
- - **One Dockerfile**: Parameterized with `BASE_IMAGE` and `RUN_CMD`; duplicate the Space and configure.
29
 
30
- ## Quick Start
 
31
 
32
- ### 1. Duplicate this Space
 
 
 
 
 
 
 
33
 
34
- Click **Duplicate this Space** on the HuggingRun Space page. After duplicating, set your Space **Repository secrets**:
35
 
36
- | Secret | Required | Description |
37
- |--------|----------|-------------|
38
- | `HF_TOKEN` | For persistence | HF token with write access ([create](https://huggingface.co/settings/tokens)) |
39
- | `HF_DATASET_REPO` | Optional | Dataset repo for backup (e.g. `your-name/HuggingRun-data`). Default: `{SPACE_ID}-data` |
40
- | `RUN_CMD` | Optional | Command to run (e.g. `python3 my_app.py`). Empty = default demo on port 7860 |
41
 
42
- ### 2. Persistence (optional)
43
 
44
- - **Manual**: Create a private Dataset repo (e.g. `your-name/HuggingRun-data`), set `HF_DATASET_REPO` and `HF_TOKEN`. Data under `PERSIST_PATH` (default `/data`) syncs every `SYNC_INTERVAL` seconds (default 60).
45
- - **Auto**: Set `AUTO_CREATE_DATASET=true` and `HF_TOKEN`; a private dataset is created on first run.
46
 
47
- ### 3. Open your Space
48
 
49
- Visit your Space URL. You’ll see the default demo (visit counter with persistence). To run your own app, set `RUN_CMD` in secrets and rebuild.
50
 
51
- ## Environment variables
52
 
53
- | Variable | Default | Description |
54
- |----------|---------|-------------|
55
- | `RUN_CMD` | (default demo) | Command to run (e.g. `uvicorn app:app --host 0.0.0.0 --port 7860`) |
56
- | `PERSIST_PATH` | `/data` | Directory to sync to the HF Dataset |
57
- | `HF_TOKEN` | — | HF token for persistence |
58
- | `HF_DATASET_REPO` | `{SPACE_ID}-data` | Dataset repo ID |
59
- | `AUTO_CREATE_DATASET` | `false` | Create dataset repo if missing |
60
- | `SYNC_INTERVAL` | `60` | Seconds between uploads |
61
- | `PORT` | `7860` | Port for default demo (HF expects 7860) |
62
 
63
- ## Hard example: FastAPI + SQLite
64
 
65
- This repo includes a **hard example** (FastAPI + SQLite) in the image. To run it:
66
 
67
- 1. In your Space **Repository secrets**, set:
68
- - `RUN_CMD=uvicorn app.fastapi_sqlite:app --host 0.0.0.0 --port 7860`
69
- 2. (Optional) Set `HF_TOKEN` and `AUTO_CREATE_DATASET=true` for persistence so the SQLite DB in `/data` survives restarts.
70
- 3. Rebuild the Space. Open the app: you’ll see a visit counter and `/api` returning JSON. Restart the Space and refresh — the count persists.
71
 
72
- This proves a stateful, “hard” app (FastAPI + SQLite + persistence) runs on HuggingRun.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
  ## License
75
 
 
19
 
20
  **Run anything on Hugging Face.**
21
 
22
+ HuggingRun 是面向 Hugging Face Spaces 的**通用部署接口**:用同一套工具解决 HF 上的持久化、单端口、网络等限制,让任意 Docker 应用都能一键部署、重启后状态保留。
23
 
24
+ - **通用工具优先**:主要维护的是通用层(持久化同步、单入口、可配置端口)。示例仅演示“最少配置”用法。
25
+ - **HF 限制与应对**:见 [docs/HF_LIMITATIONS.md](docs/HF_LIMITATIONS.md)(持久化、单端口、出站网络、DNS 等)。
26
 
27
+ ## 通用接口:你只需做两件事
 
 
28
 
29
+ 1. **Duplicate 本 Space**,在 Settings 里配好 **Secrets / Variables**(见下表)。
30
+ 2. 设置 **`RUN_CMD`** 为你要跑的进程(例如 `uvicorn app:app --host 0.0.0.0 --port 7860`);不设则运行内置 demo。
31
 
32
+ | 配置项 | 必填 | 说明 |
33
+ |--------|------|------|
34
+ | `HF_TOKEN` | 需持久化时 | HF 写权限 Token,用于把 `PERSIST_PATH` 同步到 Dataset |
35
+ | `HF_DATASET_REPO` | 可选 | 备份用 Dataset 仓库,默认 `{SPACE_ID}-data` |
36
+ | `AUTO_CREATE_DATASET` | 可选 | `true` 时自动创建私有 Dataset |
37
+ | `RUN_CMD` | 可选 | 要执行的命令;空则跑默认 demo(7860 端口) |
38
+ | `PERSIST_PATH` | 可选 | 持久化目录,默认 `/data` |
39
+ | `APP_PORT` / `PORT` | 可选 | 应用监听端口,默认 `7860`(HF 只暴露此端口) |
40
 
41
+ ## 通用工具提供了什么
42
 
43
+ - **持久化**:启动时从 HF Dataset 恢复 `PERSIST_PATH`,运行中定时上传,退出时再上传一次。解决 HF 无本地持久盘的问题。
44
+ - **单端口约定**:应用只需监听 `APP_PORT`(默认 7860);多端口服务需自己在容器内做反向代理。
45
+ - **统一入口**:同一 entrypoint 先做恢复与同步,再 `exec` 你的 `RUN_CMD`,便于任意镜像复用。
 
 
46
 
47
+ 详见 [docs/HF_LIMITATIONS.md](docs/HF_LIMITATIONS.md)
48
 
49
+ ## 示例(最小用法)
 
50
 
51
+ 示例仅展示“用通用工具、最少配置”的用法,维护量保持在最低。
52
 
53
+ ### 默认 demo
54
 
55
+ 不设 `RUN_CMD`:容器会跑一个 7860 端口的简单 HTTP 页,并把计数存到 `/data`,用于验证持久化。
56
 
57
+ ### FastAPI + SQLite
 
 
 
 
 
 
 
 
58
 
59
+ Secrets 里设:
60
 
61
+ - `RUN_CMD=uvicorn app.fastapi_sqlite:app --host 0.0.0.0 --port 7860`
62
 
63
+ (可选)设 `HF_TOKEN` `AUTO_CREATE_DATASET=true`,重启后 SQLite 数据仍在。
 
 
 
64
 
65
+ ### Ubuntu 桌面(noVNC)
66
+
67
+ 见 [ubuntu-desktop/](ubuntu-desktop/):使用同一套通用脚本(同步 + entrypoint),仅 `RUN_CMD` 改为启动 XFCE + noVNC。作为“用通用工具跑复杂应用”的示例,不做额外封装。
68
+
69
+ ## 环境变量速查
70
+
71
+ | 变量 | 默认 | 说明 |
72
+ |------|------|------|
73
+ | `RUN_CMD` | 默认 demo | 要执行的命令 |
74
+ | `PERSIST_PATH` | `/data` | 同步到 HF Dataset 的目录 |
75
+ | `HF_TOKEN` | — | 持久化用 Token |
76
+ | `HF_DATASET_REPO` | `{SPACE_ID}-data` | Dataset 仓库 |
77
+ | `AUTO_CREATE_DATASET` | `false` | 是否自动创建 Dataset |
78
+ | `SYNC_INTERVAL` | `60` | 同步间隔(秒) |
79
+ | `PORT` / `APP_PORT` | `7860` | 应用监听端口 |
80
 
81
  ## License
82
 
docs/HF_LIMITATIONS.md ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hugging Face Spaces 限制与 HuggingRun 应对
2
+
3
+ 本文档整理 [Hugging Face 官方文档](https://huggingface.co/docs/hub/en/spaces-overview) 中的 Spaces 限制,以及 HuggingRun 通用工具如何通过技术手段应对。
4
+
5
+ ---
6
+
7
+ ## 1. 持久化存储(无本地持久盘)
8
+
9
+ | 官方说明 | HuggingRun 方案 |
10
+ |----------|-----------------|
11
+ | 免费 Space 磁盘为 **非持久**:重启/停止后内容丢失。付费持久盘方案已不再对新用户开放。 | **Dataset 同步**:将 `PERSIST_PATH`(默认 `/data`)定时同步到私有 HF Dataset 仓库;启动时先拉取再启动应用。设置 `HF_TOKEN` + `AUTO_CREATE_DATASET=true` 或 `HF_DATASET_REPO` 即可。 |
12
+
13
+ 参考:[Disk usage on Spaces](https://huggingface.co/docs/hub/main/en/spaces-storage)、[Dataset storage](https://huggingface.co/docs/hub/main/en/spaces-storage#dataset-storage)
14
+
15
+ ---
16
+
17
+ ## 2. 单端口暴露
18
+
19
+ | 官方说明 | HuggingRun 方案 |
20
+ |----------|-----------------|
21
+ | 仅 **一个端口** 对外暴露,默认 `7860`,可在 README 的 `app_port` 中修改。内部可开多端口,但外部只能通过这一端口访问。 | 应用需监听 `app_port`(默认 7860)。多端口服务需在 **容器内自建反向代理**(如 Nginx/Caddy)在 7860 上统一入口,再转发到内部各端口;或使用 HuggingRun 的 `APP_PORT` 与 `RUN_CMD` 约定应用只监听该端口。 |
22
+
23
+ 参考:[Docker Spaces](https://huggingface.co/docs/hub/en/spaces-sdks-docker)
24
+
25
+ ---
26
+
27
+ ## 3. 出站网络
28
+
29
+ | 官方说明 | HuggingRun 方案 |
30
+ |----------|-----------------|
31
+ | 出站请求仅允许 **80、443、8080**。其他端口会被拦截。 | 应用只发起 HTTP/HTTPS 或 8080 出站即可。若依赖其他端口,需使用可经 443 转发的代理或 API。 |
32
+
33
+ 参考:Spaces Overview — Networking
34
+
35
+ ---
36
+
37
+ ## 4. DNS / 域名解析
38
+
39
+ | 现象 | HuggingRun 方案 |
40
+ |------|-----------------|
41
+ | 部分环境存在 DNS 解析失败或特定域名不可达(如部分 API 域名)。 | 可选:在自定义 `RUN_CMD` 前通过 **DoH(DNS over HTTPS)** 或自定义 resolver 绕过;或使用系统/语言层代理。HuggingRun 通用层可预留 `DNS_OVER_HTTPS=1` 等开关,由启动脚本在需要时配置。 |
42
+
43
+ 参考:[Issue with URL Resolution/Blocked on Hugging Face Spaces](https://discuss.huggingface.co/t/issue-with-url-resolution-blocked-on-hugging-face-spaces/95982)
44
+
45
+ ---
46
+
47
+ ## 5. 资源与休眠
48
+
49
+ | 官方说明 | HuggingRun 方案 |
50
+ |----------|-----------------|
51
+ | 免费:2 vCPU、16 GB 内存、50 GB 非持久磁盘。长时间无访问会 **Sleep**,需付费硬件才可长期运行。 | 无法通过工具绕过;建议在 README 中说明。持久化通过 Dataset 保证重启/休眠后 **状态可恢复**。 |
52
+
53
+ ---
54
+
55
+ ## 6. Cookie / iframe
56
+
57
+ | 官方说明 | HuggingRun 方案 |
58
+ |----------|-----------------|
59
+ | Space 运行在 `*.hf.space`,若被嵌入 iframe,会受浏览器跨域 Cookie 限制。 | 与通用部署接口无直接关系;若应用依赖 Cookie,需在应用层处理(如禁用 XSRF 或使用 Token)。 |
60
+
61
+ 参考:[Cookie limitations in Spaces](https://huggingface.co/docs/hub/spaces-cookie-limitations)
62
+
63
+ ---
64
+
65
+ ## 总结:HuggingRun 通用接口提供的应对
66
+
67
+ - **持久化**:`PERSIST_PATH` + HF Dataset 同步(启动恢复 + 定时上传 + 退出上传)。
68
+ - **单端口**:约定 `APP_PORT`(默认 7860),应用或自建反向代理只暴露该端口。
69
+ - **出站**:文档约束(仅 80/443/8080);复杂场景用代理或 API。
70
+ - **DNS**:可选 DoH/自定义 resolver,由启动脚本在通用层按需开启。
71
+ - **资源/休眠**:文档说明;依赖持久化保证“重启后状态保留”。
72
+
73
+ 用户只需:**设置 `RUN_CMD`(及可选 `HF_TOKEN`、`PERSIST_PATH` 等)**,即可在 HF 上部署任意兼容 Docker 的应用,由通用工具统一处理上述限制。
docs/plans/2025-03-03-ubuntu-desktop-design.md ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ubuntu 桌面版 on HuggingRun 设计
2
+
3
+ **目标**: 在 HuggingRun 上部署最新版 Ubuntu 桌面(浏览器内 noVNC 完整桌面),打通常用功能,重启后状态完整保留。
4
+
5
+ ## 方案
6
+
7
+ - **基础镜像**: Ubuntu 24.04 LTS
8
+ - **桌面**: XFCE(轻量,适合 2 vCPU / 16GB)
9
+ - **显示**: Xvfb 虚拟显示 + TigerVNC + noVNC(noVNC 监听 7860,满足 HF Spaces)
10
+ - **持久化**: 桌面用户 HOME 放在 `PERSIST_PATH`(默认 `/data/desktop-home`),由现有 sync_hf.py 同步到 HF Dataset;启动时先恢复再挂载/HOME 指向该目录
11
+ - **入口**: 独立 `ubuntu-desktop/` 目录,自有 Dockerfile;entrypoint 先执行 sync 恢复,再启动 Xvfb → 桌面 → VNC → noVNC
12
+
13
+ ## 完成标准(迭代开发)
14
+
15
+ - [ ] `ubuntu-desktop/` 可独立构建并运行,浏览器访问 7860 看到完整 XFCE 桌面
16
+ - [ ] 桌面功能可用:文件管理器、终端、浏览器(Firefox)、文本编辑器
17
+ - [ ] 设置 HF_TOKEN + AUTO_CREATE_DATASET 后,重启 Space 后桌面状态(桌面文件、配置、已装软件状态)保留,无报错
18
+ - [ ] 周期性同步与退出时同步正常,无遗漏
19
+
20
+ ## 实现要点
21
+
22
+ 1. **Dockerfile.ubuntu-desktop**: FROM ubuntu:24.04,装 python3、huggingface_hub、XFCE、TigerVNC、noVNC、Firefox;复制 HuggingRun scripts;用户 uid 1000;HOME 指向持久化目录
23
+ 2. **entrypoint_desktop**: 恢复 `/data` → 创建并绑定 `/data/desktop-home` 为桌面 HOME → 启动 sync 后台 → 启动 Xvfb、dbus、XFCE、x11vnc/tigervnc、noVNC(监听 7860)
24
+ 3. **PERSIST_PATH**: 使用 `/data`,`/data/desktop-home` 存桌面主目录;sync 继续上传/下载整个 `/data`
25
+
26
+ 日期: 2025-03-03
scripts/sync_hf.py CHANGED
@@ -27,10 +27,12 @@ os.environ.setdefault("HF_HUB_UPLOAD_TIMEOUT", "600")
27
  from huggingface_hub import HfApi, snapshot_download
28
 
29
  # ── Configuration ───────────────────────────────────────────────────────────
 
30
 
31
  HF_TOKEN = os.environ.get("HF_TOKEN")
32
  PERSIST_PATH = Path(os.environ.get("PERSIST_PATH", "/data"))
33
  RUN_CMD = os.environ.get("RUN_CMD", "")
 
34
  SYNC_INTERVAL = int(os.environ.get("SYNC_INTERVAL", "60"))
35
  AUTO_CREATE_DATASET = os.environ.get("AUTO_CREATE_DATASET", "false").lower() in ("true", "1", "yes")
36
  # In dataset we store under this path_in_repo subfolder
@@ -167,10 +169,13 @@ class GenericSync:
167
  return None
168
  print(f"[HuggingRun] Running: {cmd}")
169
  try:
170
- process = subprocess.Popen(
 
 
 
171
  cmd,
172
  shell=True,
173
- env=os.environ.copy(),
174
  stdout=sys.stdout,
175
  stderr=sys.stderr,
176
  )
 
27
  from huggingface_hub import HfApi, snapshot_download
28
 
29
  # ── Configuration ───────────────────────────────────────────────────────────
30
+ # See docs/HF_LIMITATIONS.md for how HuggingRun addresses HF Spaces limits.
31
 
32
  HF_TOKEN = os.environ.get("HF_TOKEN")
33
  PERSIST_PATH = Path(os.environ.get("PERSIST_PATH", "/data"))
34
  RUN_CMD = os.environ.get("RUN_CMD", "")
35
+ APP_PORT = os.environ.get("APP_PORT", os.environ.get("PORT", "7860")) # Single port exposed by HF
36
  SYNC_INTERVAL = int(os.environ.get("SYNC_INTERVAL", "60"))
37
  AUTO_CREATE_DATASET = os.environ.get("AUTO_CREATE_DATASET", "false").lower() in ("true", "1", "yes")
38
  # In dataset we store under this path_in_repo subfolder
 
169
  return None
170
  print(f"[HuggingRun] Running: {cmd}")
171
  try:
172
+ env = os.environ.copy()
173
+ env.setdefault("PORT", APP_PORT)
174
+ env.setdefault("APP_PORT", APP_PORT)
175
+ process = subprocess.Popen(
176
  cmd,
177
  shell=True,
178
+ env=env,
179
  stdout=sys.stdout,
180
  stderr=sys.stderr,
181
  )
ubuntu-desktop/Dockerfile ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ubuntu 24.04 Desktop on HuggingRun — noVNC on 7860, persistence via /data
2
+ FROM ubuntu:24.04
3
+
4
+ ENV DEBIAN_FRONTEND=noninteractive
5
+
6
+ # System + Python (for sync)
7
+ RUN apt-get update && apt-get install -y --no-install-recommends \
8
+ ca-certificates curl python3 python3-pip python3-venv \
9
+ && pip3 install --no-cache-dir --break-system-packages huggingface_hub \
10
+ && rm -rf /var/lib/apt/lists/*
11
+
12
+ # Desktop stack: Xvfb, XFCE, dbus, x11vnc, Firefox
13
+ RUN apt-get update && apt-get install -y --no-install-recommends \
14
+ xvfb \
15
+ xfce4 xfce4-goodies \
16
+ dbus-x11 \
17
+ x11vnc \
18
+ firefox \
19
+ procps \
20
+ && rm -rf /var/lib/apt/lists/*
21
+
22
+ # noVNC (web client on 7860)
23
+ RUN apt-get update && apt-get install -y --no-install-recommends git \
24
+ && git clone --depth 1 https://github.com/novnc/noVNC.git /opt/noVNC \
25
+ && git clone --depth 1 https://github.com/novnc/websockify /opt/noVNC/utils/websockify \
26
+ && rm -rf /var/lib/apt/lists/* /opt/noVNC/.git
27
+
28
+ # HF Spaces run as user 1000
29
+ RUN useradd -m -u 1000 user
30
+ ENV HOME=/home/user
31
+ RUN mkdir -p /data && chown user:user /data
32
+
33
+ # HuggingRun scripts (build context = repo root)
34
+ COPY scripts /scripts
35
+ COPY ubuntu-desktop/start-desktop.sh /opt/start-desktop.sh
36
+ RUN chmod +x /scripts/entrypoint.sh /opt/start-desktop.sh
37
+
38
+ ENV PERSIST_PATH=/data
39
+ ENV RUN_CMD="/opt/start-desktop.sh"
40
+ ENV DESKTOP_HOME=/data/desktop-home
41
+ ENV DISPLAY=:99
42
+ ENV VNC_PORT=5901
43
+ ENV NOVNC_PORT=7860
44
+
45
+ USER user
46
+ EXPOSE 7860
47
+ ENTRYPOINT ["/scripts/entrypoint.sh"]
ubuntu-desktop/README.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ubuntu 桌面示例
2
+
3
+ 本目录是 **HuggingRun 通用工具** 的一个示例:在 HF 上跑 Ubuntu + XFCE + noVNC,使用与主仓库相同的持久化与 entrypoint,仅 `RUN_CMD` 不同。
4
+
5
+ - **通用工具**:根目录的 `scripts/sync_hf.py`、`scripts/entrypoint.sh` 和主 Dockerfile 的 entrypoint。
6
+ - **本示例**:`Dockerfile` 在此目录,构建时从仓库根 COPY `scripts/`,并设置 `RUN_CMD=/opt/start-desktop.sh`;`start-desktop.sh` 启动 Xvfb + XFCE + x11vnc + noVNC(监听 7860),桌面 HOME 放在 `PERSIST_PATH/desktop-home`,由通用同步脚本持久化。
7
+
8
+ ## 最小用法
9
+
10
+ 1. 新建 Space 或 Duplicate HuggingRun,将 **Dockerfile** 换成本目录的 `Dockerfile` 内容(或从仓库根构建:`docker build -f ubuntu-desktop/Dockerfile .`)。
11
+ 2. 设置 Secrets:`HF_TOKEN`、可选 `AUTO_CREATE_DATASET=true`。
12
+ 3. 构建并运行,浏览器打开 Space 即可看到 noVNC 桌面;重启后状态由通用持久化保留。
13
+
14
+ 维护重点在通用层;本示例仅做最小封装。
ubuntu-desktop/start-desktop.sh ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Start Ubuntu desktop: Xvfb + XFCE + x11vnc + noVNC on 7860
3
+ # HOME is set to persistent dir by caller (sync/entrypoint). Here we ensure and use it.
4
+ set -e
5
+
6
+ export PERSIST_PATH="${PERSIST_PATH:-/data}"
7
+ export DESKTOP_HOME="${DESKTOP_HOME:-$PERSIST_PATH/desktop-home}"
8
+ export DISPLAY="${DISPLAY:-:99}"
9
+ export VNC_PORT="${VNC_PORT:-5901}"
10
+ export NOVNC_PORT="${NOVNC_PORT:-7860}"
11
+
12
+ mkdir -p "$DESKTOP_HOME"
13
+ export HOME="$DESKTOP_HOME"
14
+
15
+ # Ensure minimal XFCE dirs
16
+ mkdir -p "$HOME/.config" "$HOME/.local/share" "$HOME/Desktop"
17
+
18
+ # Start Xvfb
19
+ Xvfb "$DISPLAY" -screen 0 1280x720x24 -ac +extension GLX +render -noreset &
20
+ XVFB_PID=$!
21
+ sleep 2
22
+
23
+ # Start dbus for session (user session only; no system dbus as we run as non-root)
24
+ dbus-daemon --session
25
+
26
+ # Start XFCE (lightweight)
27
+ startxfce4 &
28
+ DESKTOP_PID=$!
29
+ sleep 3
30
+
31
+ # x11vnc: share display :99 on port 5901
32
+ x11vnc -display "$DISPLAY" -rfbport "$VNC_PORT" -forever -shared -noxdamage -bg
33
+
34
+ # noVNC: web on 7860, proxy to VNC
35
+ if [ -x /opt/noVNC/utils/novnc_proxy ]; then
36
+ /opt/noVNC/utils/novnc_proxy --listen "$NOVNC_PORT" --vnc "localhost:$VNC_PORT"
37
+ else
38
+ # Fallback: websockify + simple http serve
39
+ (cd /opt/noVNC && ./utils/novnc_proxy --listen "$NOVNC_PORT" --vnc "localhost:$VNC_PORT")
40
+ fi