rs-short 3.3.1 Docker Compose 部署指南
rs-short 3.3.1 官方 Docker 路径部署实测:无预构建镜像需 Dockerfile 本地构建、官方镜像形态强制 PostgreSQL、config.toml 密钥生成与启动红线校验、8164:8080 编排、验证码→创建→跳转→统计→钓鱼标记→删除全闭环与验收清单。
rs-short 3.3.1 Docker Compose 部署指南
目标:在真实 Linux 宿主上用官方 Docker 路径部署 rs-short 3.3.1,完成"创建短链→跳转→点击统计→钓鱼标记→删除"端到端闭环。全部命令与回显取自 2026-11-02 实测(完整取证:
/opt/data/quzuo/tmp/rs-short/evidence.md)。⚠️ 验收方式说明:实验环境无浏览器自动化,页面验收降级为 HTTP 状态码 +
<title>/正文断言 + API 闭环(curl 走的就是前端页面的同一表单端点与跳转路由,服务端行为等价)。验证码环节以"抓取 PNG→视觉识别→回填提交"方式走通,与人工输入路径一致。
一、形态纠偏:rs-short 的"官方 Docker 路径"是什么
部署前必须明确的三个实查事实(网上资料极易踩坑):
- 官方主仓不在 GitHub:
https://git.42l.fr/42l/rs-short(自建 Gitea);GitHub 的lacontrevoie/rs-short是 0 star 纯镜像,无 Releases; - 没有预构建官方镜像:Docker Hub / ghcr 均不存在
rs-short官方镜像。官方容器路径 = 用仓库自带Dockerfile本地构建; - 官方 Dockerfile 硬编码 PostgreSQL:构建命令为
cargo build --release --no-default-features --features postgres,即容器形态必须配一个 PostgreSQL;README 里的 SQLite/MySQL 只是源码编译时的 feature 备选(SQLite 官方自己都标注 "don't use in production")。
因此本指南的编排为:自建镜像(rs-short:3.3.1)+ postgres:16-alpine + 挂载 config.toml/lists.toml,两服务 compose。
二、环境信息
- 宿主:Linux x86_64
- Docker:
29.9.2;Docker Compose:v5.6.1 - rs-short:3.3.1(git tag,提交日期 2026-02-18;注意其
Cargo.toml内 version 字段仍为 3.2.1,属上游未同步,以 tag 为准) - 镜像基座:
rust:1-bookworm(builder)→debian:bookworm-slim(runtime,仅装libpq5,非 root 用户unprivileged运行) - 数据库:
postgres:16-alpine - 端口:宿主 8164 → 容器 8080(rs-short 默认监听 8080)
- 部署目录:
/opt/data/quzuo/tmp/rs-short/
三、目录准备与源码获取
mkdir -p /opt/data/quzuo/tmp/rs-short && cd /opt/data/quzuo/tmp/rs-short
# 官方主仓,锁定 3.3.1 tag
git clone --depth 1 --branch 3.3.1 https://git.42l.fr/42l/rs-short.git src
cd src && git log --oneline -1
# e90a96b Merge pull request 'mise à jour des dépendances' (#41) ...
若 git.42l.fr 不可达,可改用 GitHub 镜像 https://github.com/lacontrevoie/rs-short.git(同 tag 内容一致,实测 commit 同为 e90a96b)。
目录结构预览(与部署相关的文件):
src/
├── Dockerfile # 官方构建文件(postgres feature)
├── config.toml.sample # 配置样例 → 复制为 config.toml
├── lists.toml # 黑名单/白名单策略(镜像内也带一份,建议挂载覆盖以便修改)
├── lang.json # 四语言文案
├── assets/ # 主题、logo、背景图
├── migrations/{sqlite,postgres,mysql}/ # 启动时自动执行
└── src/ # Rust 源码(约 1000 行)
四、生成 config.toml
cd /opt/data/quzuo/tmp/rs-short
cp src/config.toml.sample config.toml
cp src/lists.toml lists.toml
必改项与实测取值(完整参数语义见 C1 篇):
config_version = 3 # 必须等于程序内置版本,过期直接 panic
[general]
listening_address = "0.0.0.0:8080" # 容器内监听
database_path = "postgres://rsshort:rsshort_dev@db:5432/rs_short" # 服务名 db 由 compose 解析
instance_hostname = "http://127.0.0.1:8164" # 生产写 https://你的域名(不带尾斜杠)
hoster_name = "QuzuoLab"
hoster_hostname = "quzuo.net"
hoster_tos = "https://quzuo.net/tos"
contact = "mailto:ops@quzuo.net"
theme = "light"
cookie_key = "<见下方生成命令>" # 必填非默认,且长度 ≥ 88
captcha_difficulty = 3 # 0 最易 / 5 最难
[phishing]
verbose_console = false
verbose_suspicious = true
verbose_level = "notice"
suspicious_click_count = 25
suspicious_click_timeframe = 12
phishing_password = "<见下方生成命令>" # 必填非默认
密钥生成(README 官方推荐方式,实测):
# cookie_key:64 字节 base64(去换行),实测长度 88,满足 ≥88 校验
openssl rand -base64 64 | tr -d '\n'
# phishing_password:URL 安全字符集,>16 字符
head -c 24 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 20
启动前校验红线(源码 init.rs::check() 实测行为):instance_hostname、cookie_key、phishing_password 任一保留样例默认值,或 cookie_key 短于 88 字符,进程直接 panic 退出——这是防裸奔设计,报错信息在容器日志里很直白。
五、docker-compose.yml(实测可用)
services:
db:
image: postgres:16-alpine
container_name: rs-short-3.3.1-db
restart: unless-stopped
environment:
POSTGRES_USER: rsshort
POSTGRES_PASSWORD: rsshort_dev
POSTGRES_DB: rs_short
volumes:
- ./pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U rsshort -d rs_short"]
interval: 5s
timeout: 3s
retries: 12
rs-short:
build:
context: ./src
dockerfile: Dockerfile
image: rs-short:3.3.1
container_name: rs-short-3.3.1-app
restart: unless-stopped
ports:
- "8164:8080"
volumes:
- ./config.toml:/run_dir/config.toml:ro
- ./lists.toml:/run_dir/lists.toml:ro
depends_on:
db:
condition: service_healthy
要点:
- 镜像构建阶段要完整编译 Rust 依赖树(actix/diesel 全家桶),首次构建耗时数分钟到十几分钟(本机有层缓存时约 2 分钟);
config.toml官方 Dockerfile 注释明确"will be missing and needs to be mounted",必须挂载;lists.toml镜像内已带默认版,挂载是为了后续改名单不用重新构建;- 健康检查门控保证 rs-short 启动时 PG 已就绪(它启动即跑迁移,连不上库会 panic 重启循环)。
六、构建与启动(实测回显)
docker compose up -d --build
首启日志(docker logs rs-short-3.3.1-app):
rs-short, starting.
initializing config.
Opening database postgres://rsshort:***@db:5432/rs_short
Running migrations
Server listening at 0.0.0.0:8080
迁移自动执行,建表结果实测(\d links):
Column | Type | 说明
id | integer PK | 自增
url_from | varchar | 短链名
url_to | varchar | 目标 URL
key | bytea | 24 随机字节 = 用户管理密钥
time | timestamp | 创建时间
clicks | integer default 0
phishing | integer default 0 | 0 正常 / 1 已标记钓鱼
注意表中没有任何 IP/UA 字段——隐私承诺属实。
七、页面验收
curl -s http://127.0.0.1:8164/ -o home.html
# HTTP 200;<title>Link shortener – QuzuoLab</title>(hoster_name 已生效)
首页含创建表单(字段 url_to / url_from / captcha)与 base64 内嵌的验证码 PNG。语言随 Accept-Language 切换,实测 fr 返回 <title>Raccourcisseur de liens – QuzuoLab</title>。
八、核心闭环实测:创建 → 跳转 → 统计 → 删除
8.1 创建短链(含验证码流程)
验证码答案存于服务端加密会话 Cookie(30 分钟有效),提交需人工/视觉识别。curl 等价流程:
# 1) 取会话 Cookie 与验证码图
curl -s -c jar.txt http://127.0.0.1:8164/ -o home.html
python3 -c "
import re,base64
h=open('home.html').read()
m=re.search(r'base64,\s*([A-Za-z0-9+/=]+)',h)
open('captcha.png','wb').write(base64.b64decode(m.group(1)))"
# 查看 captcha.png 识别 6 位字符(本例识别为 KJaRxP,比对不区分大小写)
# 2) 提交创建
curl -s -b jar.txt -o /dev/null -w "%{http_code} %{redirect_url}\n" \
-X POST http://127.0.0.1:8164/ \
--data-urlencode "url_to=https://quzuo.net/" \
--data-urlencode "url_from=quzuo-doc" \
--data-urlencode "captcha=kjarxp"
实测回显:
303 http://127.0.0.1:8164/quzuo-doc/admin/OxdoBGNVko4NDmnhY1O3z9nagSL-QuNH?created=true
管理链接只在此刻出现一次(303 的 Location 与随后管理页中),务必保存——丢失后只能去数据库捞 key 字段(见 D1 篇)。
8.2 跳转与点击统计
curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" http://127.0.0.1:8164/quzuo-doc
# 303 https://quzuo.net/
管理页 GET /quzuo-doc/admin/<key> 显示 Click count: 1;26 连击后数据库 clicks=27(每次访问 +1)。
8.3 删除
curl -s -o /dev/null -w "%{http_code}\n" "http://127.0.0.1:8164/quzuo-doc/delete/<key>"
# 200;DB 行消失;再访问短链 → 404
8.4 钓鱼标记(管理员通道)
curl -s -o /dev/null -w "%{http_code}\n" "http://127.0.0.1:8164/phish-demo/phishing/<phishing_password>"
# 200;DB phishing=1(软删)
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8164/phish-demo
# 410 —— 受害者看到反诈教育页("You have clicked on a fraudulent link…")
被标记链接的用户自助删除通道同时被禁用(返回 401),治理权收归服务端管理员。
8.5 防滥用验证
# 缩短 bit.ly → 403「Please do not chain shortened links with another link shortener.」
# 重复创建同名 → 403;验证码错误 → 400;不存在短链 → 404
九、持久化与升级
- 数据全在
./pgdata(PG 卷):实测两次docker compose restart后链接与 clicks 完好; - 升级:改 clone 的 tag →
docker compose build→up -d。迁移自动前滚,无需手工 DDL; - 备份:
pg_dump或冷备 pgdata 均可;config.toml/lists.toml是纯文本,随目录备份。
十、反向代理与 systemd(官方 wiki 要点)
官方 wiki《Integration with nginx / systemd》给出裸机路线:config.toml 监听 127.0.0.1:8080,nginx 反代 + HTTPS 终结,配 fail2ban 抓 403 日志封禁滥用 IP。容器部署等价做法:compose 中去掉 ports 改接内部网络,由宿主 nginx 反代到 127.0.0.1:8164。生产环境记得把 instance_hostname 改为对外 HTTPS 域名——注意 §8.1 的自链接防护在"域名含端口"场景有已知缺陷(见 C1 篇发现项)。
十一、验收清单
| # | 验收项 | 预期 | 实测 |
|---|---|---|---|
| 1 | 镜像构建 | rs-short:3.3.1 存在 | ✅ |
| 2 | 首启迁移 | 日志 Running migrations 无 panic | ✅ |
| 3 | 首页 | 200 + <title> 含 hoster_name | ✅ |
| 4 | 创建短链 | 303 → admin 链接 | ✅ |
| 5 | 跳转 | 303 → 目标 URL,clicks+1 | ✅(27 次) |
| 6 | 管理页 | Click count 显示 | ✅ |
| 7 | 删除 | 200 + DB 行消失 + 404 | ✅ |
| 8 | 钓鱼标记 | 200 → 访问 410 反诈页 | ✅ |
| 9 | 黑名单 | bit.ly 创建 403 | ✅ |
| 10 | 持久化 | restart 后数据完好 | ✅ |