rs-short 3.3.1 Docker Compose 部署指南

rs-short 3.3.1 官方 Docker 路径部署实测:无预构建镜像需 Dockerfile 本地构建、官方镜像形态强制 PostgreSQL、config.toml 密钥生成与启动红线校验、8164:8080 编排、验证码→创建→跳转→统计→钓鱼标记→删除全闭环与验收清单。

rs-shortrs-short短链接DockerPostgreSQL自托管2026/11/02

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 路径"是什么

部署前必须明确的三个实查事实(网上资料极易踩坑):

  1. 官方主仓不在 GitHub:https://git.42l.fr/42l/rs-short(自建 Gitea);GitHub 的 lacontrevoie/rs-short 是 0 star 纯镜像,无 Releases;
  2. 没有预构建官方镜像:Docker Hub / ghcr 均不存在 rs-short 官方镜像。官方容器路径 = 用仓库自带 Dockerfile 本地构建;
  3. 官方 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 后数据完好✅

十二、下一步

rs-short 3.3.1 Docker Compose 部署指南 - Quzuo