一些有关OpenList与Hugging Face的小巧思
本文介绍基于 Hugging Face 部署 OpenList 挂载网盘的实操经验,重点解决 HF 免费空间自动休眠导致的配置丢失问题。通过 Dockerfile 脚本实现配置自动注入,并对比了 Public 与 Private 空间在 WebDAV 挂载中的权衡
正如文章标题所述,本文的核心在于“小巧思”。它并不打算做一个全能的避风港,而是更适合那些对 OpenList 需求不大、但又希望有一个轻量云端方案的用户。如果你也想在不折腾 VPS 的前提下,优雅地解决云端部署问题,或许这篇文章可以给你一些启发。
OpenList

对 AList 有过一定了解,但没太关注社区消息的朋友,可能会好奇为什么会有 OpenList。
其实起因很简单:自从前年 AList 项目被整体出售,随后又陷入了涉嫌投毒的风波,整个社区的信任基础受到了极大的冲击。
而 OpenList 由 AList 的原贡献者基于原项目 fork 开发而成,作为一个有韧性、长期治理、社区驱动的分支,旨在防御基于信任的开源攻击。
介绍以及具体的来龙去脉就不多说了,总之,如果我们仍然希望寻找一个纯粹、可靠、且更具社区属性的挂载方案,由衷建议原 AList 用户转向 OpenList
为什么需要云端部署OpenList
说实话,在我的影音娱乐体系里,本地下载始终是绝对的主力。天翼云盘和夸克网盘的下载速度已经足够满足我的需求,这导致不管是 AList 时期,还是后来的 OpenList,这些软件对我而言只是个“备选项”。
我习惯于在 Windows 上挂着 Alist Helper,在 Android 上开着 AListLite,即用即走,轻量且本分。顺带一提,虽然这两款软件的名字仍然还带着“AList”,但现在都已经转向支持更纯粹的 OpenList 了
既然本地已经很香了,为什么突然想折腾一下云端部署?
起因是前段时间我闲来无事,重新捡起了 网易爆米花,这是一款类似 Infuse、VidHub 的播放软件,主打极其优雅的海报墙和刮削能力,旨在帮助用户打造一个完美的私人影视库。
但在使用过程中,我发现了一个尴尬的痛点:网易爆米花原生支持天翼云盘,却偏偏把夸克网盘挡在了门外。
如果想在软件里调取夸克的资源,目前唯一的办法就是通过 WebDAV 挂载。
于是我开始琢磨,要不要整一个 24 小时在线、且不占用本地资源 的 WebDAV 服务端呢?
Hugging Face

我对 Hugging Face(以下简称 HF) 其实没什么了解,我单纯将其当作了一个可以免费部署的容器化环境云平台。以下是 Gemini 对 HF 的简单介绍:
- HF 是目前全球最活跃的 AI 开源社区,被誉为“AI 界的 GitHub”。除了托管模型和数据集,它提供的 Spaces 功能允许用户通过 Docker、Streamlit 或 Gradio 快速部署 Web 应用。对于开发者而言,这本质上是一个免费且高性能的容器运行环境。
撇开这些 AI 头衔不谈,我看中的其实是它背后的“硬条件”。相比于折腾 VPS 的系统维护或是各种云服务器的计费陷阱,HF 提供的免费服务,显然足够满足我部署 OpenList 的需求了。
我不关心它有多少模型,也不关心它在社区里多有名,我只看中一点:它能让我把 OpenList 简单地“搬”到云端,作为一个 24 小时待命的 WebDAV 服务中心,随时响应网易爆米花或者其他端的调用。
部署 OpenList 到 Hugging Face
部署 OpenList 到 HF 其实也比较简单。
你可以参照我在 HF Space 里的项目LogLInk1K/openlist。
不需要研究复杂的代码,只需要点击页面右上角三个点里的 “Duplicate this Space”,直接复制我的空间环境,就能将 OpenList 服务一键部署到你自己的 HF 账号下了。
在复制(Duplicate)时,你会看到这个项目目前预设的几个环境变量:
OPENLIST_ADMIN_PASSWORD(建议填写):通过环境变量直接指定 OpenList 的管理员密码
STORAGE_JSON_1(选填):填入你从 OpenList 后台导出的第一个存储配置 JSON 字符串
STORAGE_JSON_2(选填):如果有第二个网盘,填入此处(脚本目前支持到 STORAGE_JSON_10)
如果你没有如接下来的章节里的特殊需求,那么你甚至可以不填写任何环境变量。
使用 Hugging Face 部署 OpenList 会有什么问题?
既然是“白嫖” HF 的资源,自然会有一些条条框框的限制。在实际把 HF 里的 OpenList 当作 WebDAV 服务端使用的过程中,我遇到了几个比较棘手的问题:
- 无法自定义域名
HF 的免费 Space 是不支持自定义域名的。这意味着你只能使用类似 user-repo.hf.space 这种长串的二级域名。如果你想用自己的顶级域名来访问,官方给出的唯一方案是:升级到 PRO 订阅。
对于我这种追求“白嫖”到极致的人来说,为了一个 WebDAV 服务去按月交钱,显然不符合折腾的初衷。
- 自动休眠与配置归零
这是最头疼的一点。HF 的免费实例有一个“休眠机制”:如果 48 小时内没有流量访问,容器就会自动关机。而一旦它睡着,非持久化的文件系统就会重置。
于是,一个尴尬的死循环就出现了:
- 为了省心,我把 OpenList 丢在云端
- 结果我两天没看电影,容器休眠了
- 等我第三天想看时,好不容易唤醒了它,却发现之前挂载的网盘账号、系统设置全丢了。
如果按照常规思路,每次看电影前都得去重配一遍,那这方案就不是“巧思”而是“受罪”了。
虽然 OpenList 后台自带备份与恢复功能,但每次唤醒都要登录后台、手动上传备份、等待恢复……这种割裂感完全违背了我的初衷:
“我只是想看场电影,我有什么错!”(哈哈哈哈🤣)
我要的不是一个能“手动抢救”的备份,而是一个“随时可用、无需重复配置”的影视库。
解决配置归零
我们先解决最重要的问题:伴随自动休眠所导致的配置归零。
针对我的需求来说,我基本不需要频繁调整 OpenList 的配置。通常在首次启动后,我只需要设置好密码并挂载上网盘,剩下的就是等很久以后网盘 Cookie 过期了才去更新一下。
所以我并没有选择使用监控(如 UptimeRobot 等)每隔一段时间去“戳”一下容器以维持运行。这种保活手段在互联网上虽然很常用,但对我来说更偏向“治标不治本”的外部手段。
与其费劲心思不让它“入睡”,我更倾向于让 OpenList “忘记”它曾经“初始化”过,从而实现逻辑上的“持久化配置”。
我选择了一个更原生的方案:将核心的存储配置以 JSON 格式保存在 HF 的 Secrets 中。每次容器从休眠中被唤醒启动时,通过 Dockerfile 里的逻辑,全自动地把这些配置“喂”给 OpenList。
这意味着:即便容器文件系统重置了,只要它再次启动,就会瞬间从 Secrets 中读回所有网盘挂载信息,实现“原地复活”。
如果你仔细查看了我 HF Space 里的 LogLInk1K/openlist 项目,你会发现我在 Dockerfile 编写了一套严谨的启动脚本:
CMD sh -c "\
# 1. 启动服务
./openlist server & \
PID=\$!; \
\
# 2. 等待并检查服务是否真的启动成功
echo '⏳ 正在等待 OpenList 服务启动...'; \
echo ''; \
sleep 5; \
\
MAX_RETRIES=10; \
while ! wget -q --spider http://127.0.0.1:5244/api/public/settings; do \
sleep 2; \
MAX_RETRIES=\$((MAX_RETRIES - 1)); \
if [ \$MAX_RETRIES -le 0 ]; then \
echo ''; \
echo '❌ 错误: 服务启动超时,请检查日志。'; \
exit 1; \
fi; \
done; \
echo ''; \
echo '✅ 服务已就绪!'; \
echo ''; \
\
# 3. 环境变量及密码有效性强校验
if [ -z \"\$OPENLIST_ADMIN_PASSWORD\" ]; then \
echo '❌ 错误: 未检测到 OPENLIST_ADMIN_PASSWORD。请在 Secrets 中配置!'; \
wait \$PID; exit 1; \
fi; \
\
# 4. 获取 Token
echo '🔑 正在获取 Token...'; \
RAW_TOKEN=\$(wget -qO- --timeout=5 --post-data=\"{\\\"username\\\":\\\"admin\\\",\\\"password\\\":\\\"\$OPENLIST_ADMIN_PASSWORD\\\"}\" \
--header='Content-Type: application/json' \
http://127.0.0.1:5244/api/auth/login); \
\
TOKEN=\$(echo \$RAW_TOKEN | grep -o '\"token\":\"[^\"]*\"' | cut -d'\"' -f4); \
\
# 5. 执行注入逻辑
if [ -n \"\$TOKEN\" ] && [ \"\${#TOKEN}\" -gt 20 ]; then \
echo '🚀 登录成功,正在解析配置...'; \
echo ''; \
FOUND_VALID=0; \
for i in 1 2 3 4 5 6 7 8 9 10; do \
eval JSON=\\\$STORAGE_JSON_\$i; \
\
if [ -z \"\$JSON\" ]; then \
continue; \
fi; \
\
FOUND_VALID=1; \
if echo \"\$JSON\" | grep -qv '\"mount_path\"'; then \
echo \"⚠️ 错误: STORAGE_JSON_\$i 已检测到但内容格式错误(缺少 mount_path),已跳过。\"; \
echo \"💡 提示: 请检查是否填入了正确的 JSON 字符串。\"; \
continue; \
fi; \
\
echo \"📦 正在添加第 \$i 个存储配置...\"; \
RES=\$(wget -qO- --timeout=5 --post-data=\"\$JSON\" \
--header=\"Content-Type: application/json\" \
--header=\"Authorization: \$TOKEN\" \
http://127.0.0.1:5244/api/admin/storage/create); \
echo \"\$RES\"; \
echo ''; \
done; \
\
if [ \"\$FOUND_VALID\" -eq 0 ]; then \
echo 'ℹ️ 提示: 未发现有效的存储配置。'; \
echo '👉 请检查 Secrets 中的 STORAGE_JSON_x 是否已填入内容,且变量名拼写正确。'; \
fi; \
echo '✅ 所有存储处理完成!'; \
else \
echo '❌ 登录失败!'; \
echo \"💡 响应内容: \${RAW_TOKEN:-'请求超时或无返回'}\"; \
fi; \
\
# 维持主进程
wait \$PID"为什么说这套启动脚本是严谨的?
分层解耦:我把 STORAGE_JSON_1 到 STORAGE_JSON_10 设置为环境变量。这意味着我们只需要在 HF Space 后台把网盘导出的存储配置 JSON 字符串往里一贴,容器每次启动都会自动去调 API 把网盘重新挂载好。
强校验机制:脚本里包含了对服务启动状态、管理员密码有效性、JSON 格式完整性的多重检查。它不是盲目地执行,而是确保 OpenList 后台真的“活了”才开始灌注配置。
安全性与私密性:因为存储的配置都在 HF 的 Secrets 里,即便 Space 仓库设为 Public(公开),外人也只能看到脚本,看不到具体的网盘 Cookie、Token、账户和密码。
为什么选择这样的方式?
正如上面提到的安全性与私密性,原因在于当我尝试把 Space 设为 Private(私有),想直接将敏感信息存在仓库中时,发现这会带来一个尴尬的问题:访问权限的死锁。
在 HF 的机制下,Private Space 的访问是有门槛的:
浏览器访问受限:如果你换个浏览器,或者在没登录 HF 账号的设备上打开,你会直接看到 401 错误或登录提醒,根本进不去 OpenList 的界面。
WebDAV 彻底瘫痪:这是最致命的。网易爆米花、Infuse 或者播放器是通过 WebDAV 协议去“撞”地址的。如果 Space 是私有的,HF 会在最外层加一把鉴权大锁,你的播放器根本拿不到数据,除非你在播放器端处理复杂的 HF 登录态(这几乎不可能)。
所以,为了让 WebDAV 能 24 小时随时随地调取资源,Space 必须设为 Public。
但 Public 意味着“裸奔”,仓库里的文件谁都能看。于是,我选择了“代码公开 + 存储配置存入 Secrets + 启动动态注入”这样的方案,来当成我唯一的标准答案:
对外:它是 Public 的。播放器和 WebDAV 客户端可以顺着地址直接访问到 OpenList 的服务,无需通过 HF 的账号登录拦截。
对内:它是加密的。即便别人顺着地址摸到我的代码仓库,他也只能看到一堆处理逻辑。核心的网盘 Token、账号和密码全部锁在只有我能看到的 Secrets 保险箱里。
通过 Cloudflare Workers 反代实现自定义域名
正如前面所述,HF 的免费 Space 是不支持自定义域名的。
如果你和我一样,希望在不额外支付 PRO 订阅的前提下,给 OpenList 套上一个体面的顶级域名(比如 dav.yourdomain.com)。
那么,就让我们祭出另一件“白嫖界”的神器:Cloudflare Workers。
通过 CF Workers,我们不仅能绕过 HF 的域名限制,给 OpenList 套上自己的顶级域名,还能顺便处理掉 WebDAV 常见的跨域和握手问题。
为什么要折腾这一步?
摆脱长尾域名(最主要的原因):那一串 *.hf.space 不仅难记,在某些网络环境下访问也不够顺畅。
解决 WebDAV 握手难题:避免播放器(如网易爆米花、Infuse)在连接 WebDAV 时对跨域(CORS)和请求头有严格要求,从而导致直接直连 HF 有时会因为网关限制而挂载失败的问题。
隐藏上游地址:所有的流量和请求都通过 CF 的边缘节点中转,既能加速,又能隐藏真实的 Space 运行地址。
核心代码实现
我在 CF Worker 中实现了一套逻辑,它不仅负责域名的“换壳”,更重要的是它会伪装请求头(让 HF 以为是直连访问),并注入全量的 WebDAV 跨域头:
export default {
/**
* @param {{ url: string | URL; headers: HeadersInit; method: any; body: any; }} request
* @param {any} env
*/
async fetch(request, env) {
const upstream = 'loglink1k-openlist.hf.space'; // 你的 HF 域名
const url = new URL(request.url);
// 替换域名
url.host = upstream;
url.protocol = 'https:'; // 强制 HTTPS
// 复制原始请求的 Headers,防止只读限制
const newHeaders = new Headers(request.headers);
// 注入关键 Header,让 HF 以为是直连访问
newHeaders.set('Host', upstream);
newHeaders.set('Origin', `https://${upstream}`);
newHeaders.set('Referer', `https://${upstream}`);
// 构造新的请求
const newRequest = new Request(url.toString(), {
method: request.method,
headers: newHeaders,
body: request.body,
redirect: 'follow'
});
try {
const response = await fetch(newRequest);
// 处理跨域,方便 Webdav 客户端握手
const newResponseHeaders = new Headers(response.headers);
newResponseHeaders.set('Access-Control-Allow-Origin', '*');
newResponseHeaders.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK');
newResponseHeaders.set('Access-Control-Allow-Headers', '*');
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newResponseHeaders
});
} catch (e) {
return new Response('Error: Upstream connection failed. ' + e.message, { status: 502 });
}
}
};“小巧思”的价值
至此,整套方案终于严丝合缝地闭合了:
成本 0 元:全程利用 HF 的容器额度和 CF Workers 的每日免费 10 万次请求额度,个人观影完全溢出。
原地复活:通过环境变量注入,解决了 HF 自动休眠导致的“失忆”问题。
访问体面:拥有了自定义域名,播放器刮削顺滑,海报墙秒开。
正如我在前言里说的,这套方案确实只是“小巧思”。它更适合那些对 OpenList 需求不大、但又希望有一个轻量云端方案的用户。如果你也想在不折腾 VPS 的前提下,优雅地解决云端部署问题,希望我的这段折腾经历能给你一点启发。

