Talkyard v1.2026.006 常用 CLI 命令与工具详解
Talkyard v1.2026.006 自托管运维 CLI 全景实测:prod-one 随附备份/调度/升级/观测/站长找回脚本逐条详解(含 backup.sh 完整归档回显)、docker compose 常规命令、psql/redis-cli/ES curl 直查与维护端点探针。
Talkyard v1.2026.006 常用 CLI 命令与工具详解
Talkyard 自托管的官方运维通道是 talkyard-prod-one 仓库(ty-prod-one-v1 分支)scripts/ 目录下的一组 Bash 脚本,加上 docker compose 常规命令与 psql/redis-cli/ES curl 三个后端直查入口。本文逐条讲解这些工具的子命令、参数与内部逻辑,并给出在 v1.2026.006-fa49d1cee 实测环境上跑出的真实回显(含一次完整手动备份的归档产物清单)。部署与配置背景分别见 部署指南 与 配置参数详解。
一、环境信息
| 项目 | 值 |
|---|---|
| 宿主机 | Ubuntu 24.04.5 LTS |
| Docker / Compose | 29.9.2 / v5.6.1 |
| Talkyard 版本 | v1.2026.006-fa49d1cee(RELEASE_BRANCH=tyse-v1-regular) |
| 部署目录 | /opt/data/quzuo/tmp/talkyard/talkyard-v1(官方惯例 /opt/talkyard-v1) |
| Compose 项目名 | talkyard-v1-2026-006-stack |
| 访问入口 | http://localhost:8139 |
| 备份目录 | /var/opt/backups/talkyard/v1(backup 容器挂载) |
命令示例均假定在部署目录执行;跨目录时给 compose 命令加 -p talkyard-v1-2026-006-stack。
二、工具概览
prod-one 仓库 scripts/ 目录实测清单(jsdelivr 拉取,2026-11-02):backup.sh、delete-old-backups.sh、find-admin-login-link.sh、prepare-os.sh、schedule-automatic-upgrades.sh、schedule-daily-backups.sh、upgrade-if-needed.sh,另有 impl/(含 unjson.sh)、offsite-backup-checks/、old/、os-conf/、tests/ 子目录;仓库根目录还有 view-logs、view-stats 两个快捷脚本。
| 类别 | 工具 | 用途 |
|---|---|---|
| 备份 | backup.sh / delete-old-backups.sh | 起一次性 backup 容器做全量备份 / 清理过期归档 |
| 调度 | schedule-daily-backups.sh / schedule-automatic-upgrades.sh | 向 crontab 注入每日备份与自动升级任务 |
| 升级 | upgrade-if-needed.sh | 检查版本分支、备份、拉镜像、维护页切换的完整升级流程 |
| 观测 | view-logs / view-stats | 日志(去 JSON 化)/ 容器资源占用 |
| 排障 | find-admin-login-link.sh | 数据库里找回站长账号与一次性登录链接 |
| 前置 | prepare-os.sh | 宿主机 sysctl 等 OS 层准备 |
| 常规 | docker compose | 生命周期管理与进入容器 |
| 直查 | psql / redis-cli / curl(ES) | 后端组件数据与健康检查 |
解读:脚本全部是薄封装——备份/清理用 docker compose run --rm backup 把活交给容器内的 /ty/*.sh,观测用 compose 原生命令加管道;真正复杂的是 upgrade-if-needed.sh(247 行)。因此学 Talkyard CLI 的重点是理解"宿主机脚本 → compose → 容器内脚本"三层转发关系。
三、前置条件
- 已按 部署指南 完成栈部署且服务 healthy;
- 当前用户在宿主机有 docker 权限,且在部署目录(或正确使用
-p); - 已 clone talkyard-prod-one(
ty-prod-one-v1分支)——scripts/impl/unjson.sh、versions/子模块等被脚本依赖;若采用最小化目录(只放 compose 文件),view-logs的 unjson 管道与升级脚本不可用,本文第六、七节会标注实测影响; - 调度类脚本会写 crontab,需要 root 或目标用户权限。
四、prod-one 随附脚本详解
4.1 backup.sh —— 手动/定时全量备份
脚本全文(8 行,commit e4fdd4b 2026-03-21 "Add backup scripts that start the backup container"):
#!/bin/bash
label="${1:-manual}"
exec /usr/bin/docker compose run --rm backup \
/ty/backup.sh "$label" "$(hostname)" "$(date '+%FT%H%MZ' --utc)"
参数:$1 为归档标签,缺省 manual;随后两参数由脚本自动取主机名与 UTC 时间戳。等效命令实测(本实验目录无 scripts/,直接执行其核心行):
$ docker compose run --rm backup /ty/backup.sh manual "$(hostname)" "$(date '+%FT%H%MZ' --utc)"
Created Postgres password file: /tmp/.pgpass.
Now running CMD: /ty/backup.sh manual master 2026-11-02T1057Z
2026-11-02T10:57:38+00:00 backup-script: Backing up 'master', when: '2026-11-02T1057Z', tag: 'manual' ...
2026-11-02T10:57:38+00:00 backup-script: Generated random test-that-backups-work value: 'CJllJKUS93J3JRxU64d2'
2026-11-02T10:57:38+00:00 backup-script: Backing up config to: /var/opt/backups/talkyard/v1/archives/master-2026-11-02T1057Z-manual-config.tar.gz ...
INSERT 0 1
2026-11-02T10:57:38+00:00 backup-script: Done backing up config.
2026-11-02T10:57:38+00:00 backup-script: Backing up Postgres to: /var/opt/backups/talkyard/v1/archives/master-2026-11-02T1057Z-manual-postgres.sql.gz ...
INSERT 0 1
2026-11-02T10:57:38+00:00 backup-script: Done backing up Postgres.
2026-11-02T10:57:38+00:00 backup-script: Backing up Redis to: /var/opt/backups/talkyard/v1/archives/master-2026-11-02T1057Z-manual-redis.rdb.gz ...
INSERT 0 1
2026-11-02T10:57:38+00:00 backup-script: Done backing up Redis.
2026-11-02T10:57:38+00:00 backup-script: Backing up uploads to: /var/opt/backups/talkyard/v1/archives/master-uploads-up-to-incl-2026-11.d ...
2026-11-02T10:57:38+00:00 backup-script: Done backing up uploads.
INSERT 0 1
cp: cannot stat '/opt/talkyard-v1/docs/how-restore-backup.md': No such file or directory
2026-11-02T10:57:38+00:00 backup-script: Done backing up.
归档产物实测:
$ ls -la /var/opt/backups/talkyard/v1/archives/
-rw-r----- 1 root root 0 Nov 2 18:57 HOW-RESTORE-BACKUPS.md
-rw-r----- 1 root root 7505 Nov 2 18:57 master-2026-11-02T1057Z-manual-config.tar.gz
-rw-r----- 1 root root 70996 Nov 2 18:57 master-2026-11-02T1057Z-manual-postgres.sql.gz
-rw-r----- 1 root root 21 Nov 2 18:57 master-2026-11-02T1057Z-manual-random-value.txt
-rw-r----- 1 root root 146 Nov 2 18:57 master-2026-11-02T1057Z-manual-redis.rdb.gz
drwxr-x--- 2 root root 4096 Nov 2 18:57 master-uploads-up-to-incl-2026-11.d
解读:一次备份产出四件套——配置 tar.gz、Postgres SQL dump、Redis RDB、上传文件目录快照,命名规则 <主机>-<UTC时间>-<标签>-<内容>。随机校验值(random-value.txt)会被写进数据库再导出,恢复后可验证备份真正可用。cp: cannot stat .../how-restore-backup.md 是实验目录未按官方路径 /opt/talkyard-v1 全量 clone 仓库导致的无害报错(所以 HOW-RESTORE-BACKUPS.md 为 0 字节),备份主体不受影响。
4.2 delete-old-backups.sh —— 过期归档清理
脚本全文(单行 exec):
#!/bin/bash
exec /usr/bin/docker compose run --rm backup /ty/delete-old-backups.sh
解读:与 backup.sh 同构,把清理逻辑交给容器内 /ty/delete-old-backups.sh 按保留策略删除旧归档。注意先确认新备份可用再执行清理(官方 offsite-backup-checks/ 目录即为此设计)。
4.3 schedule-daily-backups.sh —— 每日备份调度
脚本向 crontab 注入两行(实测环境外,官方路径 /opt/talkyard-v1):
10 2 * * * cd /opt/talkyard-v1 && ./scripts/backup.sh daily >> talkyard-maint.log 2>&1
10 3 * * * cd /opt/talkyard-v1 && ./scripts/delete-old-backups.sh >> talkyard-maint.log 2>&1
解读:02:10 备份、03:10 清理,间隔 1 小时保证清理跑在备份之后;输出统一追加到部署目录的 talkyard-maint.log。部署目录不是 /opt/talkyard-v1 时,脚本里的 cd 路径需要相应调整。
4.4 schedule-automatic-upgrades.sh —— 自动升级调度
向 crontab 注入:
10 4 * * * cd /opt/talkyard-v1 && ./scripts/upgrade-if-needed.sh >> talkyard-maint.log 2>&1
解读:04:10 检查升级,与备份(02:10)、清理(03:10)串成每日维护流水线,保证升级失败时前一小时已有可恢复的备份。
4.5 upgrade-if-needed.sh —— 完整升级流程(247 行)
升级脚本的实测逻辑链(按执行顺序):
- 读
.env的RELEASE_BRANCH,校验必须含-v1-(v1 分支保护,防止误切到实验分支); git submodule对versions子模块 fetch 并checkout -B到 RELEASE_BRANCH;tail -n1 versions/version-tags.log取NEXT_VERSION;若.env设了PINNED_VERSION_TAG则以其为准(钉版本跳过升级);- 升级前先
./scripts/backup.sh备份当前版本,并记入previous-version-tags.log; docker system prune --filter label=talkyard.edition=tyse清理一年前的旧版镜像;docker compose pull拉新镜像;- 依次
stop search→stop app→down; up -d app只先起 app,挂 ty-maint 维护页容器(TY_MAINT_MODE=true时把 maint-msg.html 覆盖到 502.html,并有 wrong-app-ip override),站点访问者看到维护提示而非报错;- 轮询 app 容器内
curl --noproxy '*' localhost:9000/-/are-scripts-ready直到就绪; compose up -d拉起全部服务,sed回写.env的VERSION_TAG。
第 9 步端点实测回显:
$ docker compose exec app curl -s --noproxy '*' -w '\nhttp_code=%{http_code}\n' localhost:9000/-/are-scripts-ready
6 script engines have been created, fine
http_code=200
解读:/-/are-scripts-ready 是 app 的脚本引擎就绪探针,升级时 DB migration 在 app 启动中执行,此端点 200 才放流量;--noproxy '*' 是必需的——app 容器全局 http_proxy 指向 egressp,本地回环请求不走代理反而会被 SSRF 代理拦下。
4.6 view-logs 与 view-stats —— 观测快捷方式
view-logs 全文:
docker compose logs $@ | scripts/impl/unjson.sh
view-stats 全文:
docker stats `docker ps --format '{{.Names}}'`
Talkyard 的 app 日志是 JSON 行格式,裸看 compose logs 可读性差,view-logs 通过管道 impl/unjson.sh 把 JSON 展开成可读文本。未做 JSON 化的原始输出实测:
$ docker compose logs --tail 2 app
app-1 | {"eventTime":"2026-11-02T10:45:34.449Z","message":"s1: Deleting 1 posts from the index queue... [TyMSIX_DELIXD]","severity":"DEBUG"}
app-1 | {"eventTime":"2026-11-02T10:54:21.110Z","message":"s1: PubSub: Pupl presence @tyadmin #100: Away [TyDPRESCNS]","severity":"TRACE"}
解读:每行都是带 eventTime/severity/message 的 JSON,消息里普遍带 Ty*/Dw* 错误码前缀(如 TyMSIX_DELIXD、TyDPRESCNS),排障时拿这些码搜官方源码是最快路径。view-stats 实测输出(docker stats --no-stream,MemUsage/容器内存占比):
app 0.66% 1.159GiB/15.57GiB 7.45%
web 0.00% 33.08MiB 0.21%
cache 0.10% 13.89MiB 0.09%
rendr 0.00% 672KiB 0.00%
rdb 0.14% 116.8MiB 0.73%
search 0.31% 1.178GiB/1.6GiB 73.65%
egressp 0.00% 6.938MiB 0.04%
解读:search 顶在 override 设定的 1.6G limit 上(73.65%),app 堆 512M 但 RSS 1.159GiB——这两项就是内存预算的大头,见配置篇 6.3 节。
4.7 find-admin-login-link.sh —— 站长账号找回
脚本逻辑(实测逐条验证):
- psql 查
users3表is_admin为真且user_id >= 100(排除内置 System/Sysbot)的用户,列出站长邮箱; - psql 查
emails_out3表近 1 天type in (22,23)的 outbox 记录——22=ResetPassword、23=OneTimeLoginLink——把密码重置/一次性登录链接捞出来直接访问; - 提示可用
/-/admin-login端点再生成一次性登录链接。
第 1 步同款查询实测回显:
$ docker compose exec rdb psql -U talkyard -d talkyard -c "select user_id, username, is_admin from users3 where is_admin and user_id >= 100 order by user_id"
user_id | username | is_admin
---------+----------+----------
100 | tyadmin | t
(1 row)
解读:站长 tyadmin 的 user_id 恰为 100,即系统首个真实用户。忘记站长邮箱或登录不上时,这个脚本 + outbox 表是自托管 Talkyard 的"物理钥匙"——注意前提是 SMTP 或 outbox 里有邮件记录。
4.8 prepare-os.sh —— 宿主机 OS 准备
首次装机执行的一次性脚本:设置 net.core.somaxconn 等 sysctl(8192 级别)、准备系统参数。compose 与 nginx 配置里的 [BACKLGSZ] 注释(listen ... backlog=8192 需与 somaxconn 一致)即与其对应。本文实验环境已预先配置,未重复执行,故不引用其内容细节。
五、Docker Compose 常规命令
5.1 生命周期与状态
$ docker compose ps --format '{{.Service}}\t{{.Status}}'
app Up 26 minutes (healthy)
cache Up 42 minutes (healthy)
egressp Up 42 minutes (healthy)
rdb Up 42 minutes (healthy)
rendr Up 42 minutes
search Up 42 minutes (healthy)
web Up 42 minutes (healthy)
常用组合:up -d 启动全栈(backup 服务因 profiles: [backup] 不会随启动);stop / start 不删容器;down 删容器留卷;down -v 连数据卷一起删(销毁性操作,生产禁用前先备份);rm app + up -d app 是改环境变量的必做重建动作(见配置篇 3.3)。
5.2 进入容器与配置核验
$ docker compose config --images
debiki/talkyard-search:v1.2026.006-fa49d1cee
debiki/talkyard-app:v1.2026.006-fa49d1cee
debiki/talkyard-rendr:v1.2026.006-fa49d1cee
debiki/talkyard-cache:v1.2026.006-fa49d1cee
debiki/talkyard-rdb:v1.2026.006-fa49d1cee
debiki/talkyard-egressp:v1.2026.006-fa49d1cee
debiki/talkyard-web:v1.2026.006-fa49d1cee
$ docker compose exec -T app sh -c 'env | grep -E "TALKYARD|BECOME|PLAY_HEAP|no_proxy|http_proxy" | sort'
BECOME_OWNER_EMAIL_ADDRESS=admin@talkyard.io
http_proxy=http://egressp:4750
no_proxy=''
PLAY_HEAP_MEMORY_MB=512
TALKYARD_HOSTNAME=localhost
TALKYARD_SECURE=false
解读:config --images 核验 .env 插值结果(升级后确认新 tag 已生效);exec -T(禁伪 TTY)适合脚本内取数。exec 进交互调试:docker compose exec app bash。
六、后端组件直查命令
6.1 PostgreSQL(rdb 容器)
$ docker compose exec rdb psql -U talkyard -d talkyard -c 'select version()'
PostgreSQL 18.6 (Debian 18.6-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
解读:口令无需手输——rdb 容器内走 trust/peer 体系且 POSTGRES_PASSWORD_FILE 已挂 secret;查参数如 shared_buffers 实测为 16384(8kB 页即 128MB)。业务表前缀 dw_*/user_*/pages 等,users3、emails_out3 见 4.7。
6.2 Redis(cache 容器)
$ docker compose exec cache redis-cli ping
PONG
$ docker compose exec cache redis-server --version
Redis server v=8.6.1 sha=00000000:0 malloc=jemalloc-5.3.0 bits=64 build=16af4e2dbf02df6b
解读:Redis 在栈里承担会话/缓存,数据在 redis-data 卷(实测新站 0B);无认证(仅内网 be/fe_int_net 可达),不要把它暴露出容器网络。
6.3 Elasticsearch(search 容器)
$ docker compose exec search curl -s localhost:9200/_cluster/health
{"cluster_name":"TyEsCluster","status":"green","timed_out":false,"number_of_nodes":1,"number_of_data_nodes":1,"active_primary_shards":1,"active_shards":1,"relocating_shards":0,"initializing_shards":0,"unassigned_shards":0,"unassigned_primary_shards":0,"delayed_unassigned_shards":0,"number_of_pending_tasks":0,"number_of_in_flight_fetch":0,"task_max_waiting_in_queue_millis":0,"active_shards_percent_as_number":100.0}
解读:单节点集群 TyEsCluster 状态 green、1 分片全 active。搜索功能冒烟可用 /-/search API(实测高亮回显见部署指南);索引类问题看 app 日志中 TyMSIX_* 码。
6.4 app 内部端点
$ docker compose exec app curl -s --noproxy '*' -w '\nhttp_code=%{http_code}\n' localhost:9000/-/are-scripts-ready
6 script engines have been created, fine
http_code=200
解读:app 容器内监听 9000(web 反代到它);容器内 curl 一律加 --noproxy '*',否则被 egressp 代理拦截。运维相关端点还有 /-/admin-login(一次性链接)、/-/login-password(口令登录)等。
七、注意事项
- 备份容器不常驻:
backup服务带profiles: [backup],docker compose ps永远看不到它,必须经compose run --rm backup触发——backup.sh 就是为此存在。 - crontab 路径假设:两个 schedule 脚本注入的命令行
cd /opt/talkyard-v1,非官方目录部署要改脚本或做软链,否则定时任务静默失败(只留 talkyard-maint.log 线索)。 - 升级有维护窗口:upgrade-if-needed.sh 第 7-9 步期间站点返回维护页(502.html),自动升级时间要选低峰。
- PINNED_VERSION_TAG 是升级刹车:钉住版本后自动升级跳过;灰度验证完记得清空,否则永远停在旧版。
- 最小化目录的脚本缺口:未全量 clone 仓库时
view-logs的 unjson.sh、docs/how-restore-backup.md(备份时 cp 报错)、versions/子模块(升级脚本依赖)都不可用,生产务必完整 clone。 - down -v 不可逆:会删掉 pg-data/es-data 等全部卷,执行前先跑一次 backup.sh 并把归档移出主机。
八、常见问题
Q1:./scripts/backup.sh 报 "no such service: backup"?
A:compose 项目名不对(没在部署目录执行且未加 -p),backup 服务定义在主 compose 文件里;确认 docker compose config --services | grep backup 能看到它。
Q2:docker compose logs app 刷屏全是 JSON?
A:用仓库根目录的 ./view-logs app(内部管道 scripts/impl/unjson.sh 展开);或在管道后接 grep '"severity":"ERROR"' 过滤。
Q3:upgrade-if-needed.sh 直接退出?
A:检查 .env 的 RELEASE_BRANCH 是否含 -v1-(脚本第一步的分支保护),以及 versions/ 子模块是否已初始化(git submodule update --init)。
Q4:忘记站长邮箱 / 无法登录后台?
A:跑 ./scripts/find-admin-login-link.sh:从 users3 拿站长名单,从 emails_out3 捞近 1 天的重置/一次性登录邮件链接(实测站长查询回显见 4.7);outbox 无记录时用 /-/admin-login 流程重新生成。
Q5:定时备份没产生新归档?
A:看部署目录下 talkyard-maint.log;crontab 注入的 cd /opt/talkyard-v1 与实际目录不符是最常见原因(见第七节)。
Q6:备份日志里 cp: cannot stat '/opt/talkyard-v1/docs/how-restore-backup.md' 要紧吗?
A:实测无害——只影响归档里附带的恢复说明文档(生成 0 字节 HOW-RESTORE-BACKUPS.md),四件套数据归档完整;按官方路径完整 clone 仓库即消除。
Q7:容器内 curl 访问 localhost 被拒?
A:app 容器全局 http_proxy=http://egressp:4750,本地请求加 --noproxy '*'(实测见 6.4)。
九、参考链接
- 部署仓库(脚本所在):https://github.com/debiki/talkyard-prod-one (分支 ty-prod-one-v1)
- 源码与镜像 Dockerfile:https://github.com/debiki/talkyard
- 官方文档:https://docs.talkyard.io
- Compose 规范:https://github.com/compose-spec/compose-spec/blob/main/spec.md
- 站内相关:Talkyard 介绍与选型指南、Docker Compose 部署指南、配置参数详解