在 Android Termux 上搭建 SillyTavern 并通过 frp 映射公网端口
本文记录一次完整实操:通过 SSH 远程管理一台运行 Termux 的安卓设备,在其上部署 SillyTavern(AI 聊天前端),再用 frp 把服务映射到公网端口。涉及真实地址、账号密码的部分均以占位符代替,请自行替换。
一、背景与目标
我有一台运行 Termux 的安卓设备(在 NAT 后面),另有一台带公网 IP 的 frps 服务端。设备通过 frpc 主动连上 frps 建立隧道,从而可以远程 SSH 管理、对外提供服务。
本次要做两件事:
- 在设备上安装并运行 SillyTavern(Node.js 应用,默认监听
127.0.0.1:8000)。 - 通过 frp 把 8000 端口映射到公网 7777 端口,实现手机/电脑浏览器直接访问。
二、环境速览
远程设备(Termux)环境:
| 项目 | 值 |
|---|---|
| 系统 | Android(Linux 4.19,aarch64) |
| 终端 | Termux |
| Node.js | v24.17.0 |
| npm | 11.18.0 |
| frp | 0.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.js、package.json、public、src、data 等。
用 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 即可使用。
十、安全建议
- 立刻设置登录密码:服务一旦映射到公网,任何人都能访问。首次打开页面就设置 SillyTavern 的用户名/密码,别裸奔。
- frp token 务必保密:
frpc.toml里的 token 等同隧道通行证,泄露等于把设备管理权交给别人。 - 开启白名单:SillyTavern 的
config.yaml支持whitelistMode,可只允许指定 IP 访问,进一步缩小暴露面。 - SSH 改用密钥认证:管理通道同样经过 frp,建议用密钥替代密码,并关闭密码登录。
- 注意设备保活:安卓设备重启后,需保持 Termux 应用不被系统清理,runit 服务才会自动恢复。
总结
整条链路一句话:设备上跑 SillyTavern(127.0.0.1:8000)→ frpc 隧道 → frps → 公网 7777。
本次实操的关键收获:
- GitHub 直连慢时,用 gh-proxy 等加速镜像下载 tarball,比 git clone 快一个数量级;
- npm 依赖换 npmmirror 国内镜像,安装速度提升明显;
- frp 隧道重启会“断自己的手”,操作前要有心理预期;
- 用 runit 管理服务,让进程崩溃、设备重启后都能自动拉起。