Uguu 1.9.9 Docker Compose 部署指南
用官方仓库自带 Dockerfile 自建 Uguu 1.9.9 镜像并以 Docker Compose 部署:源码获取与构建期网络适配、Compose 编排(数据双卷/配置挂载/端口子网规划)、上传下载闭环验证、数据备份与 cron 过期清理,含 vendor 缺失、/files/ 404 等排障实录,实测通过。
Uguu 1.9.9 Docker Compose 部署指南
目标:在装有 Docker 的服务器上,用官方仓库自带的 Dockerfile 构建 Uguu 1.9.9 镜像,并以 Docker Compose 完成单机部署:覆盖源码获取、构建期网络适配、Compose 编排(数据持久化、配置挂载、端口与子网规划)、启动验证、上传下载闭环测试、数据备份与日常运维。已在 Ubuntu 24.04 LTS(x86_64)+ Docker 29.9.2 + Docker Compose v2 环境实测通过(2026-11-01)。
一、环境信息
- 服务器:Ubuntu 24.04 LTS(x86_64),Docker 29.9.2,Docker Compose v2
- 软件版本:Uguu 1.9.9(官方仓库 master,
package.json与src/config.json双渠道确认,2026-11-01 实查) - 镜像形态:Uguu 无官方预构建镜像(Docker Hub 无 nokonoko 官方仓库),官方仓库自带
Dockerfile(Alpine 3.20 底座 + Nginx 1.26.3 + PHP-FPM 8.3 + Bun 构建链,内置 SQLite 自动初始化与健康检查),本文按官方 Dockerfile 本地构建镜像uguu:1.9.9 - 端口规划:宿主机 8134 → 服务容器内 8080(镜像内 Nginx 监听 8080)
- 网络规划:自定义 bridge 子网
10.89.56.0/24(显式指定,避开默认网段冲突) - 实测镜像体积:357MB(构建完成后
docker images显示)
二、部署形态说明:为什么从源码构建
Uguu 与多数 Go 系文件分享组件(Plik/PicoShare/goploader 一个二进制即可跑)不同,它是构建型 PHP 项目:源码仓库里的 EJS 模板、JS、CSS 必须经 Bun 工具链编译进 dist/ 目录后才是可部署的站点,PHP 依赖需经 Composer 安装。官方仓库在 1.9.9 起自带 Dockerfile(发行注记「Added docker dev environment support」),把「Alpine 底座 + apk 装 PHP 8.3 全家桶 + Bun 编译 + Composer 装依赖 + Nginx/PHP-FPM 双进程启动 + SQLite 自动建库」整条链封装在镜像构建中,这是官方目前唯一的一键部署路径,也是本文采用的方案。
前置依赖(宿主机只需要 Docker,构建过程全部发生在构建容器内):
- 网络可达:
codeload.github.com(拉源码)、dl-cdn.alpinelinux.org(apk 依赖)、registry.npmjs.org(Bun 依赖)、getcomposer.org(Composer)、repo.packagist.org(PHP 依赖)。2026-11-01 实测以上站点直连均可用;⚠️ 网络说明:本环境raw.githubusercontent.com直连被阻断(TLS 握手无响应),而官方 Makefile 的 Composer 安装步骤恰要从该域名下载安装器,因此构建前需按第四章打网络适配补丁——补丁后全程仅依赖getcomposer.org,实测可用。
三、获取源码并打网络适配补丁
mkdir -p /opt/uguu && cd /opt/uguu
curl -sL -o uguu-master.tar.gz https://codeload.github.com/nokonoko/Uguu/tar.gz/refs/heads/master
tar xzf uguu-master.tar.gz --strip-components=1
ls
实测回显(解压后的仓库根目录):
compress.sh docker Dockerfile LICENSE Makefile package.json php_cs.dist
pngcompression.sh README.md RELEASE_NOTE.md src
编辑 Makefile,把 install 与 install-dev 两个 target 中「下载 composer-setup.php 安装器」的三行:
cd $(DESTDIR)/ && $(CURL) -o composer-setup.php https://raw.githubusercontent.com/composer/getcomposer.org/main/web/installer
cd $(DESTDIR)/ && $(PHP) composer-setup.php --quiet
cd $(DESTDIR)/ && rm composer-setup.php
替换为一行直接下载 Composer 官方 phar(install-dev target 中无 --quiet 变体同理替换):
cd $(DESTDIR)/ && $(CURL) -fsSL -o composer.phar https://getcomposer.org/composer-stable.phar
⚠️ 排障实录 ①:替换行必须保留行首 TAB。Makefile 的 recipe 行以 TAB 开头;若替换时不小心把行首 TAB 丢掉,make 会把
cd $(DESTDIR)/ && $(CURL) ... composer.phar整行解析成一串名为cd、&&、composer.phar、https的伪 target(构建日志出现成串Makefile:124: warning: overriding recipe for target 'composer.phar'),后续php composer.phar update也随伪 target 一起被吞掉——构建照常「成功」,但dist/vendor/autoload.php不会生成,部署后所有上传请求 500(DEBUG打开后显示Undefined array key之外的 autoload fatal)。实测中首次构建即踩中该坑,修 TAB 后重建解决。验证方法见第六章的vendor检查。
四、编写 Dockerfile 路径对齐与 Compose 编排
4.1 Compose 项目文件
创建 docker-compose.yml(项目名显式写为 uguu-1.9.9-stack,避免多项目同名互删):
name: uguu-1.9.9-stack
services:
uguu:
image: uguu:1.9.9
container_name: uguu-1.9.9-web
ports:
- "8134:8080"
volumes:
- uguu-files:/var/www/files
- uguu-db:/var/www/db
- ./config/config.json:/var/www/uguu/dist/config.json:ro
restart: unless-stopped
networks:
default:
name: uguu-1.9.9-net
driver: bridge
ipam:
config:
- subnet: 10.89.56.0/24
volumes:
uguu-files:
uguu-db:
设计说明:
- 数据双卷:上传文件(
/var/www/files)与 SQLite 数据库(/var/www/db)各自独立命名卷,备份与迁移可以分开处理;镜像内 entrypoint 启动时会用jq读取config.json的DB_PATH/FILES_ROOT,目录不存在则创建、库文件不存在则用官方 schema 自动初始化; - 配置挂载:
config.json以只读方式挂到/var/www/uguu/dist/config.json(应用运行时唯一读取的配置位置,Classes 经 APCu 缓存键UC加载),改配置只需改宿主机文件并重启服务,无需重建镜像; - 端口/子网:8134 与
10.89.56.0/24均为实测环境规划值,读者可按需替换,但 compose 的name必须保持uguu-1.9.9-stack形态(组件+版本+stack)以便识别。
4.2 构建期配置文件
创建 config/config.json(最小可用配置,完整参数逐项解释见《Uguu 1.9.9 配置参数详解》;注意 DOMAIN/FILE_DOMAIN 决定了 API 返回的下载 URL,请按实际访问地址填写):
{
"dest": "dist",
"pkgVersion": "1.9.9",
"pages": ["index.ejs", "faq.ejs", "api.ejs", "404.ejs"],
"DEBUG": false,
"BENCHMARK_MODE": false,
"max_upload_size": 128,
"expireTime": 30,
"expireTimeUnit": "minutes",
"siteName": "Uguu Lab",
"subTitle": "Temporary file hosting",
"DOMAIN": "127.0.0.1:8134",
"FILE_DOMAIN": "127.0.0.1:8134/files",
"abuseContact": "abuse@example.com",
"infoContact": "info@example.com",
"ServerCountryLocation": "China",
"SiteMetaInfo": "Uguu Lab is a temporary file hosting service!",
"responseTime": "24 hours",
"showGithub": true,
"donationBanner": false,
"paypalUrl": "",
"bitcoinAddress": "",
"ethereumAddress": "",
"kofiUrl": "",
"malwareBanner": false,
"DB_MODE": "sqlite",
"DB_PATH": "/var/www/db/uguu.sq3",
"DB_USER": "NULL",
"DB_PASS": "NULL",
"LOG_IP": false,
"ANTI_DUPE": true,
"BLACKLIST_DB": false,
"FILTER_MODE": true,
"RATE_LIMIT": false,
"RATE_LIMIT_TIMEOUT": 60,
"RATE_LIMIT_FILES": 100,
"FILES_ROOT": "/var/www/files/",
"FILES_RETRIES": 15,
"NAME_LENGTH": 8,
"ID_CHARSET": "abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ",
"DOUBLE_DOTS_EXTENSIONS": ["tar.bz2", "tar.gz", "min.css", "min.js", "conf.bak"],
"FILTER_EXTENSIONS": ["exe", "scr", "com", "vbs", "bat", "cmd", "htm", "html", "jar", "msi", "apk", "phtml", "svg"],
"FILTER_MIME": ["application/msword", "text/html", "application/x-dosexec", "application/java", "application/java-archive", "application/x-executable", "application/x-mach-binary", "image/svg+xml"],
"GRILLS": ["2", "3", "4", "5"]
}
4.3 Nginx 文件下载链路修复补丁(必做)
⚠️ 排障实录 ②(上游缺陷):官方
docker/nginx.conf的静态资源正则 location 会抢占/files/前缀 location,导致图片类上传文件按 URL 访问 404。
官方 docker/nginx.conf 中同时存在两处相关 location:
location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|avif)$ {
expires 30d;
access_log off;
}
location /files/ {
alias /var/www/files/;
autoindex on;
access_log off;
}
Nginx 的匹配规则是:先记下最长前缀匹配,再按顺序查正则,命中正则即直接采用(除非前缀带 ^~)。因此 .png 结尾的请求会落到静态资源 location,用 root /var/www/uguu/dist/public 拼路径找 dist/public/files/xxx.png——实测该文件不存在,返回 404(nginx error.log 实锤:open() "/var/www/uguu/dist/public/files/bkBnnvRE.png" failed (2: No such file or directory));而 .txt 不在正则里,走 /files/ 别名返回 200。实测取证(修复前):
GET /files/wnBwPUJr.txt → 200
GET /files/bkBnnvRE.png → 404
修复:编辑 docker/nginx.conf,给 /files/ 加 ^~ 前缀(跳过正则检查):
location ^~ /files/ {
alias /var/www/files/;
autoindex on;
access_log off;
}
五、构建镜像并启动
5.1 构建
docker build -t uguu:1.9.9 .
构建过程要点(实测日志节选,构建总耗时约 1 分钟,其中 Bun 依赖 2035 个包安装 7.9s、make 编译约 2s、Composer 依赖安装约 30s):
#6 [ 2/14] RUN apk add --no-cache ... nginx php83 php83-fpm ... composer ...
#6 DONE ...
#11 [ 7/14] RUN bun install --ignore-scripts && make development && make install-dev ...
#11 0.158 bun install v1.4.4
#11 7.999 Resolved, downloaded and extracted [2035]
#11 8.052 556 packages installed [7.89s]
#11 DONE 10.3s
#19 naming to docker.io/library/uguu:1.9.9 done
构建完成后核对产物完整性(vendor 目录即排障实录 ① 的验证点):
docker run --rm --entrypoint sh uguu:1.9.9 -c \
'test -f /var/www/uguu/dist/vendor/autoload.php && echo "vendor OK"; \
ls /var/www/uguu/dist/'
实测回显:
vendor OK
classes composer.json composer.lock composer.phar config.json vendor ...
public checkExpire.php upload.php
5.2 启动与状态确认
docker compose up -d
docker compose ps
实测回显:
Container uguu-1.9.9-web Created
Container uguu-1.9.9-web Starting
Container uguu-1.9.9-web Started
NAME IMAGE STATUS
uguu-1.9.9-web uguu:1.9.9 Up 4 seconds (health: starting)
镜像自带健康检查(curl -fsS http://127.0.0.1:8080/,30s 间隔),半分钟后 STATUS 转为 Up (healthy)。
六、部署验证
6.1 Web 首页
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8134/
curl -s http://127.0.0.1:8134/ | grep -oE "<title>[^<]*"
实测回显:
200
<title>Uguu · Temporary file hosting
⚠️ 注意:页面
<title>与「Max upload size is …」提示文案来自构建期编译的静态 HTML(构建时用的是仓库默认src/config.json:上限 128 MiB、过期 8 hours),运行时挂载的config.json只影响后端行为。改站点文案需改src/config.json后重新构建,详见配置篇排障实录。
6.2 上传-下载闭环(含二进制校验)
printf 'hello uguu' > hello.txt
curl -s -F "files[]=@hello.txt" http://127.0.0.1:8134/upload.php
实测回显:
{
"success": true,
"files": [
{
"hash": "47272535fb3d837e",
"filename": "wnBwPUJr.txt",
"url": "https:\/\/127.0.0.1:8134\/files\/wnBwPUJr.txt",
"size": 22,
"dupe": false
}
]
}
⚠️ 排障实录 ③:响应里的
url是硬编码https://前缀拼FILE_DOMAIN(源码Upload::uploadFile()固定写死)。本地 HTTP 实测时把https://手动改成http://即可下载;生产环境给站点配 TLS 后此问题自然消失。
按返回的 filename 下载并做二进制校验(换用一张真实 PNG,源文件与下载文件 md5 应一致):
curl -s http://127.0.0.1:8134/files/bkBnnvRE.png -o downloaded.png
md5sum photo.png downloaded.png
实测回显(location ^~ 补丁后):
93ca32a536da1698ea979f183679af29 photo.png
93ca32a536da1698ea979f183679af29 downloaded.png
6.3 数据库与文件落位确认
docker exec uguu-1.9.9-web sqlite3 /var/www/db/uguu.sq3 ".tables"
docker exec uguu-1.9.9-web sqlite3 /var/www/db/uguu.sq3 \
"SELECT id, hash, originalname, filename, size, date FROM files ORDER BY id;"
docker exec uguu-1.9.9-web ls -la /var/www/files/
实测回显:
accounts blacklist files ratelimit
1|47272535fb3d837e|hello.txt|wnBwPUJr.txt|22|1793506876
2|32962fc36316ee0c|photo.png|bkBnnvRE.png|67|1793506876
-rw-r--r-- 1 nginx nginx 22 Nov 1 04:21 wnBwPUJr.txt
-rw-r--r-- 1 nginx nginx 67 Nov 1 04:21 bkBnnvRE.png
四张表(accounts/blacklist/files/ratelimit)由官方 schema 自动建好;文件属主为 nginx(镜像内 Nginx/PHP-FPM 运行用户),权限 0644。
七、数据持久化、备份与到期清理
- 备份对象:命名卷
uguu-1.9.9-stack_uguu-db(SQLite 单文件uguu.sq3)与uguu-1.9.9-stack_uguu-files(上传文件目录)。SQLite 库建议用sqlite3 /var/www/db/uguu.sq3 ".backup '/var/www/db/backup.sq3'"做一致性快照后再拷贝,避免直接 cp 正在写入的库文件; - 到期清理:容器内没有 cron,必须由外部定时任务触发
checkExpire.php。官方裸机方案是 root crontab 每分钟执行;在 Compose 部署下,在宿主机 crontab 里加一条即可:
* * * * * docker exec uguu-1.9.9-web php /var/www/uguu/dist/checkExpire.php
实测该脚本行为:无过期文件时输出 No file(s) expired, nothing to do.;有过期文件时静默删除(物理文件 + files 表记录一并清掉,同时清理 24 小时前的 ratelimit 记录)。完整实测过程见《Uguu 1.9.9 常用命令详解》。
八、日常运维命令
# 查看服务日志(Nginx access/error + PHP-FPM 汇总到容器 stdout/stderr)
docker compose logs -f --tail 100 uguu
# 修改 config.json 后使其生效(APCu 缓存了配置,必须重启 PHP-FPM 所在容器)
docker compose restart uguu
# 重建镜像(升级版本:改 tag 与 compose 中 image 后重复第三章~第五章)
docker compose down && docker build -t uguu:1.9.10 . && docker compose up -d
# 彻底清理(含数据卷,谨慎)
docker compose down -v
九、参考链接
- 官方部署 Wiki(裸机 Nginx + PHP-FPM 全流程):https://github.com/nokonoko/Uguu/wiki/Uguu-Configuration-%26-Installation
- 官方仓库与 Dockerfile:https://github.com/nokonoko/Uguu
- 本系列选型篇:《Uguu 1.9.9 介绍与选型指南》
- 本系列配置篇:《Uguu 1.9.9 配置参数详解》
- 本系列命令篇:《Uguu 1.9.9 常用命令详解》