在 Android Termux 上搭建 SillyTavern 并通过 frp 映射公网端口

本文记录一次完整实操:通过 SSH 远程管理一台运行 Termux 的安卓设备,在其上部署 SillyTavern(AI 聊天前端),再用 frp 把服务映射到公网端口。涉及真实地址、账号密码的部分均以占位符代替,请自行替换。

一、背景与目标

我有一台运行 Termux 的安卓设备(在 NAT 后面),另有一台带公网 IP 的 frps 服务端。设备通过 frpc 主动连上 frps 建立隧道,从而可以远程 SSH 管理、对外提供服务。

本次要做两件事:

  1. 在设备上安装并运行 SillyTavern(Node.js 应用,默认监听 127.0.0.1:8000)。
  2. 通过 frp 把 8000 端口映射到公网 7777 端口,实现手机/电脑浏览器直接访问。

二、环境速览

远程设备(Termux)环境:

项目
系统Android(Linux 4.19,aarch64)
终端Termux
Node.jsv24.17.0
npm11.18.0
frp0.70.1(通过 pkg/apt 安装)
已装工具git、curl、wget、tar、pkg

管理机为 Windows,使用 PuTTY 套件中的 plink 做免交互 SSH 登录。

三、SSH 连接与检查环境

3.1 plink 免交互登录

Windows 自带 OpenSSH 需要手动输密码,改用 plink 可直接把密码作为参数传入:

plink -ssh -P <SSH端口> -l <用户名> -pw <密码> -batch <服务器公网IP> "uname -a; whoami"

首次连接时 plink 会因为 host key 未缓存而拒绝连接,并打印服务器指纹,例如:

The server's ssh-ed25519 key fingerprint is:
  ssh-ed25519 255 SHA256:<服务器SSH公钥指纹>

确认无误后用 -hostkey 参数直接指定指纹即可(以后不再询问):

plink -ssh -P <SSH端口> -l <用户名> -pw <密码> \
  -hostkey 'SHA256:<服务器SSH公钥指纹>' -batch <服务器公网IP> "uname -a; whoami; pwd"

返回示例:

Linux localhost 4.19.157-perf-g8e1dce01cadc ... aarch64 Android
<用户名>
/data/data/com.termux/files/home

3.2 检查工具链

node -v          # v24.17.0
npm -v           # 11.18.0
git --version
pkg list-installed | grep -E "nodejs|frp|git"

3.3 小技巧:复杂命令用 base64 传输

PowerShell 把命令字符串传给远程 bash 时,引号、$ 等符号经常互相打架。更稳的做法:先在本地把整段脚本 base64 编码,再在远程解码执行,彻底规避转义问题:

$script = @'
echo "PATH=$PATH"
for c in git curl wget tar pkg; do
  command -v $c >/dev/null 2>&1 && echo "FOUND:$c" || echo "MISSING:$c"
done
'@
$b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($script))
plink -ssh -P <SSH端口> -l <用户名> -pw <密码> -hostkey 'SHA256:<指纹>' -batch <服务器公网IP> "echo $b64 | base64 -d | bash"

四、下载 SillyTavern

4.1 GitHub 直连太慢,先做测速

直接 git clone 只有几十 KB/s,几乎无法完成。先批量测试官方与几个加速通道的速度(每个 10 秒):

for url in \
  "https://codeload.github.com/SillyTavern/SillyTavern/tar.gz/refs/heads/release" \
  "https://ghfast.top/https://github.com/SillyTavern/SillyTavern/archive/refs/heads/release.tar.gz" \
  "https://gh-proxy.com/https://github.com/SillyTavern/SillyTavern/archive/refs/heads/release.tar.gz" \
; do
  echo "URL: $url"
  curl -s -L -o /dev/null -r 0-1048576 --max-time 10 \
    -w "  code=%{http_code} speed=%{speed_download}B/s size=%{size_download}\n" "$url"
done

本次实测结果:

  • codeload 官方通道:约 27 KB/s ❌
  • ghfast.top:约 3.3 MB/s
  • gh-proxy.com:约 8.8 MB/s ✅(10 秒已下载 38MB)

4.2 通过加速镜像下载源码包

选最快的镜像下载 release 分支的 tarball,解压并改名:

cd ~
curl -s -L -o SillyTavern.tar.gz \
  "https://gh-proxy.com/https://github.com/SillyTavern/SillyTavern/archive/refs/heads/release.tar.gz"
tar -xzf SillyTavern.tar.gz
mv SillyTavern-release SillyTavern
ls ~/SillyTavern

目录下应能看到 server.jspackage.jsonpublicsrcdata 等。

用 tarball 而不是 git clone:一是能走 CDN 加速、速度快一个数量级,二是不需要完整 git 历史。以后更新直接重新下载解压即可,但务必先备份 data 目录(里面是用户数据)。

五、安装依赖

设备上 npm 官方源实测约 1.3 MB/s,换成国内镜像后约 8 MB/s。用 nohup 后台安装,避免 SSH 会话中断导致安装失败:

cd ~/SillyTavern
nohup npm install --registry=https://registry.npmmirror.com --no-audit --no-fund \
  > ~/npm-install.log 2>&1 &

本次约 20 秒完成:

added 829 packages in 20s

期间可另开一个会话用 tail -f ~/npm-install.log 观察进度。

六、首次启动

后台启动并把日志写到文件:

cd ~/SillyTavern
nohup node server.js > ~/sillytavern.log 2>&1 &

首次启动会自动把内置预设复制到 data/default-user 下,然后编译前端(webpack),大约十几秒:

Content file presets/instruct/Mistral V3-Tekken.json copied to data/default-user/instruct
...
Compiling frontend libraries...
webpack 5.105.4 compiled successfully in 17011 ms

SillyTavern is listening on IPv4: 127.0.0.1:8000

Go to: http://127.0.0.1:8000/ to open SillyTavern

本地验证:

curl -s -o /dev/null -w "local http code=%{http_code}\n" http://127.0.0.1:8000
# local http code=200

默认只监听 127.0.0.1,这是正确做法:SillyTavern 不直接暴露公网,由 frp 隧道转发出去,避免裸奔。

七、frp 公网映射

7.1 原理回顾

frp 分两端:

  • frps(服务端):跑在有公网 IP 的机器上,负责接收公网流量;
  • frpc(客户端):跑在设备上,主动连接 frps,把本机端口“挂”到公网指定端口。

我的 frps 已就绪(公网 7000 端口,带 token 鉴权)。设备上原来的 ~/frpc.toml 只映射了 SSH:

# ~/frpc.toml
serverAddr = "<frps公网IP>"
serverPort = 7000
auth.token = "<你的frpToken>"

[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 8022
remotePort = <SSH公网端口>

7.2 新增 SillyTavern 映射

frpc.toml 末尾追加一条代理,把本机 8000 映射到公网 7777:

[[proxies]]
name = "sillytavern"
type = "tcp"
localIP = "127.0.0.1"
localPort = 8000
remotePort = 7777

7.3 重启 frpc(这里有个大坑)

我的 frpc 由 runit 服务管理(runsvdir 监视 /data/data/com.termux/files/usr/var/service)。直接 sv restart frpc 会报“unable to change to service directory”,因为服务目录不在默认路径,需要写全路径:

sv restart /data/data/com.termux/files/usr/var/service/frpc

⚠️ 关键坑:SSH 隧道本身也是 frp 转发的,重启 frpc 会瞬间断开你正在使用的 SSH 连接。 不要慌,runsv 会自动把它重新拉起,等十几秒重新连上即可。

也可以直接杀掉 frpc 子进程,让 runsv 自动拉起并加载新配置:

ps aux | grep frpc
kill <frpc的PID>

重启后查看日志(/data/data/com.termux/files/usr/var/log/frpc/current),确认两条代理都成功:

proxy added: [sillytavern ssh]
[ssh] start proxy success
[sillytavern] start proxy success

八、让 SillyTavern 也纳入 runit 自启动

之前 SillyTavern 是用 nohup 手动拉起的,设备重启后不会自动恢复。frpc 走 runit,照葫芦画瓢给 SillyTavern 也建一个服务:

SVC=/data/data/com.termux/files/usr/var/service/sillytavern
mkdir -p $SVC

cat > $SVC/run <<'EOF'
#!/data/data/com.termux/files/usr/bin/sh
exec 2>&1
cd /data/data/com.termux/files/home/SillyTavern
exec /data/data/com.termux/files/usr/bin/node server.js
EOF

chmod +x $SVC/run

runsvdir 会自动发现新服务目录并拉起。然后停掉原先 nohup 启动的实例:

pkill -f "node server.js"
sleep 10
ps aux | grep "node server.js"    # 应看到 runsv sillytavern 名下的新进程
curl -s -o /dev/null -w "local http code=%{http_code}\n" http://127.0.0.1:8000
# local http code=200

这样进程崩溃或被 kill 后都会自动重启,和 frpc 同一套管理机制。

九、公网验证

回到本地电脑,直接访问公网地址:

curl -s -o /dev/null -w "HTTP code: %{http_code}\n" http://<服务器公网IP>:7777
# HTTP code: 200

再确认页面标题:

curl -s http://<服务器公网IP>:7777 | grep -o "<title>.*</title>"
# <title>SillyTavern</title>

至此,SillyTavern 已通过 frp 映射到公网 7777 端口,浏览器打开 http://<服务器公网IP>:7777 即可使用。

十、安全建议

  1. 立刻设置登录密码:服务一旦映射到公网,任何人都能访问。首次打开页面就设置 SillyTavern 的用户名/密码,别裸奔。
  2. frp token 务必保密frpc.toml 里的 token 等同隧道通行证,泄露等于把设备管理权交给别人。
  3. 开启白名单:SillyTavern 的 config.yaml 支持 whitelistMode,可只允许指定 IP 访问,进一步缩小暴露面。
  4. SSH 改用密钥认证:管理通道同样经过 frp,建议用密钥替代密码,并关闭密码登录。
  5. 注意设备保活:安卓设备重启后,需保持 Termux 应用不被系统清理,runit 服务才会自动恢复。

总结

整条链路一句话:设备上跑 SillyTavern(127.0.0.1:8000)→ frpc 隧道 → frps → 公网 7777

本次实操的关键收获:

  • GitHub 直连慢时,用 gh-proxy 等加速镜像下载 tarball,比 git clone 快一个数量级;
  • npm 依赖换 npmmirror 国内镜像,安装速度提升明显;
  • frp 隧道重启会“断自己的手”,操作前要有心理预期;
  • 用 runit 管理服务,让进程崩溃、设备重启后都能自动拉起。