如果你需要在自己的服务器上搭一个「给第三方用户下载文件」的服务——比如给客户分发安装包、给群友分享资源、给终端用户提供固件下载——用 Docker 几分钟就能搞定。本文整理一套完整的部署方案,覆盖 Caddy(纯静态下载站)、Filebrowser(网页网盘)、Dufs(极速分发 + WebDAV) 三种主流选择,并给出经过实际部署验证的配置与避坑要点。
💡 先分清需求:如果你是自己下载 BT/PT 种子、HTTP 直链(比如 qBittorrent、Aria2),那属于「下载工具」;本文讲的是给别人提供下载服务——用户通过浏览器或
wget/curl直接拉取你服务器上的文件。
三方案速览
| 方案 | 定位 | 界面 | 权限管理 | 适合场景 |
|---|---|---|---|---|
| Caddy | 纯静态 HTTP 下载站 | 极简目录列表 | 可公开 / 可 HTTP 基础认证 | 文件公开分发、脚本批量拉取 |
| Filebrowser | 网页网盘 | 现代网盘 UI(预览/编辑/上传) | 内置多用户角色 + 分享链接 | 需要图形化管理、发分享链接 |
| Dufs | 静态服务器 + WebDAV | 极简目录列表 + 搜索 | 命令行规则控制(可按路径分配) | 极致轻量、WebDAV 挂载 |
方案一:Caddy(纯静态下载站)
Caddy 是最简单、性能最好的文件分发方案:把目录映射进去,浏览器打开就是目录列表,点谁下载谁,也能用 wget/curl 批量拉取。
1.1 公开模式(无需账号密码)
这是最常用的形态:任何人不登录就能浏览目录、下载文件,适合公开资源分发。
第一步:创建目录结构
mkdir -p downloads
cd downloads # 后续所有文件都在这个目录里操作第二步:编写 Caddyfile(与 docker-compose.yml 同级)
:8081 {
root * /srv
file_server browse
}🔑
:8081是容器内部监听端口,宿主机用 8081 映射。file_server browse开启目录列表(不带browse就只能访问已知路径,看不到列表页)。
第三步:编写 docker-compose.yml
services:
caddy:
image: caddy:alpine
container_name: download_server
ports:
- 8081:8081
volumes:
- ./downloads:/srv # 要提供下载的文件目录
- ./Caddyfile:/etc/caddy/Caddyfile
restart: unless-stopped第四步:启动
docker compose up -d把文件放进 downloads/,访问 http://服务器IP:8081/ 就能看到目录列表并直接下载。用命令行拉取也很方便:
wget http://服务器IP:8081/filename.zip
curl -O http://服务器IP:8081/filename.zip1.2 密码认证模式(basicauth)
如果不想完全公开,Caddy 内置 basicauth 指令:浏览器访问时弹出登录框,输入账号密码才能浏览下载。
第一步:生成密码哈希
Caddy 出于安全考虑不允许在配置里写明文密码,必须先把它转成 bcrypt 哈希。用 Docker 临时容器生成(示例密码 123456,务必换成你自己的强密码):
docker run --rm caddy:alpine caddy hash-password --plaintext "123456"输出一串以 $2a$14$ 开头的字符串(不同机器每次生成的哈希不同,这是正常的),复制它。
第二步:修改 Caddyfile(把 $2a$14$... 替换成你刚复制的那串哈希)
:8081 {
basicauth /* {
admin $2a$14$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
}
root * /srv
file_server browse
}第三步:重启生效
docker compose restart caddy现在访问站点会先弹登录框,输入 admin 和明文密码(如 123456)才能看到下载目录。
⚠️ 注意:
basicauth的哈希值只认你caddy hash-password生成的那串;换一台机器、换一个密码都要重新生成。忘记密码就重新跑一次第一条命令,更新 Caddyfile 里的哈希即可。
1.3 绑定域名 + 自动 HTTPS
Caddy 最大的优势是自动申请和续签 Let’s Encrypt / ZeroSSL 证书——你只要把域名解析过来,Caddy 全自动搞定 HTTPS,无需手动配置 SSL。
第一步:DNS 解析
在域名解析商处添加 A 记录,把域名(如 dl.example.com)指向服务器公网 IP,等待生效(可以用 ping dl.example.com 确认解析成功)。
第二步:修改 docker-compose.yml
services:
caddy:
image: caddy:alpine
container_name: download_server
ports:
- 80:80 # 用于 HTTP 自动跳转 HTTPS 及 ACME 证书验证
- 443:443 # HTTPS 访问端口
volumes:
- ./downloads:/srv
- ./Caddyfile:/etc/caddy/Caddyfile
- ./caddy_data:/data # 必须:持久化保存自动申请的 SSL 证书
- ./caddy_config:/config
restart: unless-stopped🔑
./caddy_data:/data必须加:证书存在/data里,如果不挂载,容器每次重建都会重新申请证书,容易触发 Let’s Encrypt 的频率限制。
第三步:修改 Caddyfile(把站点地址从 :8081 换成你的域名)
dl.example.com {
root * /srv
file_server browse
}第四步:重建并启动
docker compose down
docker compose up -d等 10 秒左右,访问 https://dl.example.com/,地址栏会出现安全锁。如果上一节配置过 basicauth,把它加回到 Caddyfile 里即可(哈希值不用重新生成)。
1.4 443 被占用:用自定义端口跑 HTTPS
很多服务器上 443 端口已经被其他服务(如 sing-box、反代)占用,这时可以给 HTTPS 换一个端口,比如 8443。
前提:Caddy 申请证书走 HTTP-01 验证,必须保证 80 端口空着且公网可达(哪怕最终访问走 8443)。
第一步:修改 Caddyfile(域名后直接加端口)
dl.example.com:8443 {
root * /srv
file_server browse
}第二步:修改 docker-compose.yml(保留 80,加上 8443)
services:
caddy:
image: caddy:alpine
container_name: download_server
ports:
- 80:80 # 必须保留:用于申请/续签证书
- 8443:8443 # 自定义 HTTPS 端口
volumes:
- ./downloads:/srv
- ./Caddyfile:/etc/caddy/Caddyfile
- ./caddy_data:/data
- ./caddy_config:/config
restart: unless-stopped第三步:重建生效
docker compose down
docker compose up -d之后通过 https://dl.example.com:8443/ 访问,同样带安全锁。防火墙/安全组记得同时放行 80 和 8443。
1.5 进阶:sing-box SNI 分流(443 被 sing-box 占用的优雅解法)
既然 443 在 sing-box 手里,与其换端口,不如让 sing-box 当「前置路由」:在 sing-box 的 TLS 规则里按 SNI 识别——当用户请求的是 dl.example.com 时,直接把流量转发给内网 Caddy 的端口。这样用户端始终用标准的 https://dl.example.com/ 访问,无需记忆后缀端口。
具体做法是给 sing-box 的 443 入站开启 TLS 嗅探(sniff),在路由规则中匹配 server_name == dl.example.com 的流量,dial 到内网 Caddy 的监听地址(如 10.0.0.8:8443,示例内网地址);其余流量按原规则继续走代理。
💡 具体规则写法取决于你的 sing-box 版本和路由架构,核心思路是「按 SNI 分流 + fallback」,避免 443 端口打架。sing-box 的详细配置不属于本文范围,这里只给方向。
方案二:Filebrowser(网页网盘 + 多用户管理)
如果除了下载,你还想要图形化上传/管理文件、给用户生成带密码或有效期的分享链接,Filebrowser 是最佳选择:Go 编写,资源占用极低,自带现代网盘界面。
第一步:准备目录
mkdir -p downloads database第二步:编写 docker-compose.yml
services:
filebrowser:
image: filebrowser/filebrowser:latest
container_name: filebrowser
user: 1000:1000 # 与宿主机目录属主 UID/GID 保持一致(见下方避坑)
ports:
- 8081:80 # 端口冲突就换一个,如 8082:80
volumes:
- ./downloads:/srv
- ./database:/database
environment:
- FB_DATABASE=/database/filebrowser.db
restart: unless-stopped第三步:启动
docker compose up -d第四步:获取登录密码
访问 http://服务器IP:8081/,出现登录页。注意:新版本(2.63+)已经没有默认密码 admin/admin 了——首次启动时系统会随机生成一个管理员密码并打印在容器日志里:
docker logs filebrowser | grep password输出类似:User 'admin' initialized with randomly generated password: xxxxxxxx,复制这串随机密码,用用户名 admin + 这串密码登录。登录后建议立刻在「设置」里改掉,或者执行下面命令重置:
docker exec filebrowser filebrowser users update admin --password 你的新密码⚠️ 避坑 1:默认密码不是 admin/admin。网上大量教程(包括一些较新的)仍写「默认账号密码都是 admin」,那是老版本行为。新版为了安全改用随机密码,且强制密码 ≥ 12 位、禁止
admin/password这类常见弱密码。⚠️ 避坑 2:目录权限。
user: 1000:1000意味着容器进程以 UID 1000 运行,宿主机上downloads/和database/目录必须允许 UID 1000 读写。若日志报permission denied,执行chown -R 1000:1000 downloads database后再重启。如果你的宿主机用户本身不是 1000(比如是 1001),把user: 1000:1000改成对应的 UID:GID。
使用:登录后可在网页上传文件、创建文件夹;选中文件可生成「分享链接」(可设密码和有效期)发给第三方;也可以创建只读用户,让第三方直接登录浏览下载。
方案三:Dufs(极速分发 + WebDAV)
Dufs 是 Rust 编写的静态文件服务器,主打极致轻量,原生支持 WebDAV,权限通过命令行参数按「路径 + 账号」控制,非常适合「匿名可下载、管理员可上传管理」的读写分离场景。
第一步:编写 docker-compose.yml
services:
dufs:
image: sigoden/dufs:latest
container_name: dufs
ports:
- 5000:5000
volumes:
- ./downloads:/data
# 权限规则说明:
# -a admin:123456@/:rw → 账号 admin(密码 123456)对根目录读写
# -a @/ → 匿名用户对根目录只读(浏览+下载)
# --allow-upload → 全局放开上传(配合 admin 的 :rw 才真正能写)
command: /data --allow-search --allow-upload -a admin:123456@/:rw -a @/
restart: unless-stopped第二步:启动
mkdir -p downloads
docker compose up -d第三步:验证
- 匿名访问
http://服务器IP:5000/:能看到目录列表并直接下载,但不能上传。 - 管理员通过 WebDAV 上传:
curl -u admin:123456 -T local.zip http://服务器IP:5000/(密码记得换成强密码)。
⚠️ 避坑(权限规则的真实语义):网上很多教程写
-a admin:123456@/就声称「admin 拥有全局读写、其他人可浏览下载」——实测这是错的:
-a admin:123456@/省略权限后缀时,admin 默认只有只读(:ro),要读写必须写:rw;- 只配置账号规则后,匿名访问直接 401,并不会「默认所有人可读」——要公开下载必须再叠加一条
-a @/;- 即使给了
:rw,Dufs 还有全局权限开关:不配--allow-upload就无法上传、不配--allow-delete就无法删除。正确写法是上面 compose 里那一行:
-a admin:123456@/:rw -a @/+--allow-upload。
Filebrowser vs Dufs 怎么选?
| 维度 | Filebrowser | Dufs |
|---|---|---|
| 底层与性能 | Go,性能优秀,占用极低 | Rust,性能极致,占用几乎可忽略 |
| 核心定位 | Web 端文件管理器 / 私有网盘 | 静态文件服务器 / WebDAV 服务器 |
| UI 界面 | 现代网盘 UI,支持图片预览、文本编辑、视频播放 | 极简目录列表,提供基础搜索 |
| 用户与权限 | 内置 SQLite,多用户角色、权限后台 | 无数据库,命令行参数按路径+账号控制 |
| 分享链接 | 支持生成含密码/有效期的分享链接 | 直接分享真实 URL 路径 |
| WebDAV | 官方不支持 | 原生支持且稳定,可挂载为本地磁盘 |
选 Filebrowser:需要图形界面管理文件、给用户发带密码/限期的分享链接、有多用户需求。 选 Dufs:追求极致轻量、需要 WebDAV 挂载(把远程目录当本地磁盘)、要命令行精确控制权限。
补充:WebDAV 是什么?
WebDAV 是一种基于 HTTP 的文件传输和管理协议,核心作用是把远程服务器上的文件目录直接「挂载」到本地设备,当成本地磁盘一样使用。Dufs 原生支持它。
相比 SMB/FTP 的优势:走 HTTP 协议,防火墙友好(一般只开一个端口)、无需额外端口、天然支持 HTTPS 加密,跨平台(Windows/macOS/Linux/手机都支持)。Windows 里「映射网络驱动器」、macOS 里「连接服务器」填入 WebDAV 地址即可挂载。
实战验证
本文所有配置均在本机 Docker 环境(Docker 29.2.1)实际部署验证,结果如下:
| 场景 | 配置 | 实测结果 |
|---|---|---|
| Caddy 公开下载站 | :8081 + file_server browse | 首页 200,目录列表正常,hello.txt 免登录直接下载 ✓ |
| Caddy basicauth | caddy hash-password 生成哈希 + basicauth | 无凭据 401、错误密码 401、正确凭据 200 ✓ |
| Filebrowser | user: 1000:1000 + FB_DATABASE | 日志输出随机密码,登录成功,文件列表正常 ✓ |
| Dufs 读写分离 | -a admin:123456@/:rw -a @/ + --allow-upload | 匿名浏览/下载 200,admin WebDAV PUT 201,匿名写 401 ✓ |
验证中发现的坑(已写进上文避坑):
- Filebrowser 2.63.x 首次启动不再默认 admin/admin,而是随机生成密码打印到日志;
- Dufs
-a user:pass@/权限后缀省略=只读,且加规则后匿名不再可读,需-a @/显式公开; - Filebrowser
user: 1000:1000与宿主机目录属主不匹配时(如宿主机 UID 1001)会报permission denied,需chown对齐; - Caddy 的域名 + HTTPS 部分依赖真实域名与公网 80 端口,本文在本地以 HTTP 模式验证了配置正确性,实际申请证书请按 1.3/1.4 节操作。
总结
| 需求 | 推荐 |
|---|---|
| 纯公开分发、脚本批量拉取 | Caddy 公开模式(1.1) |
| 要账号密码保护 | Caddy basicauth(1.2) |
| 要 HTTPS + 域名 | Caddy 1.3,443 被占就 1.4 |
| 要网页管理 + 分享链接 | Filebrowser(方案二) |
| 要 WebDAV + 极致轻量 | Dufs(方案三) |
三种方案可以共存(不同端口),按需组合即可。动手时记得:所有示例密码(123456)都换成自己的强密码,域名 dl.example.com 换成你自己的。
