手机变服务器:Termux + proot Ubuntu 个人服务器全栈部署完全指南
一台闲置的 Android 手机,跑起 Termux + proot-distro 的 Ubuntu 容器,就能承载博客、图库、搜索引擎、词典、网盘、对象存储、内网穿透、代理、数据库等十几个服务。本文从零开始,把环境搭建、核心服务部署、公网访问与运维排错整合成一份完整指南。
1. Termux 入门与基础配置
Termux 是安卓无 root 运行的 Linux 终端模拟器,可以把一台闲置手机变成便携式 Linux 服务器。
1.1 安装与首次配置
版本注意:不要安装 Google Play 上的旧版 Termux,建议从 F-Droid 或 GitHub Releases 下载最新 APK,系统需 Android 7.0+。
装好之后,按顺序做三件事:
# 1. 更新软件包 |
授权存储后,内部存储创建 storage 目录,Termux 通过 ~/storage 访问手机文件。
1.2 保活(防止锁屏被杀)
termux-wake-lock # 启用唤醒锁 |
Android 系统层面,还需要:设置 → 应用 → Termux → 电池 → 无限制;多任务卡片锁定 Termux;系统自启动管理中允许 Termux。安卓 12+ 务必加入电池白名单,否则后台容易被杀。
1.3 常用命令速查
pkg install <包名> # 安装软件 |
注意事项:Termux 不能直接运行 APK;不要用 sudo(没有真正 root);卸载 Termux 会清空全部内部数据;不要随意执行来路不明的脚本。
1.4 proot-distro Ubuntu 容器
pkg update && pkg install -y proot proot-distro tar |
Termux .bashrc文件:
alias on='proot-distro login ubuntu --bind /storage/emulated/0/Ubuntu:/root/home' |
首次进入设置 DNS 与国内 ARM 源:
echo "nameserver 8.8.8.8" > /etc/resolv.conf |
在 Termux 宿主设置 alias 一键登录:
echo "alias ubuntu='proot-distro login ubuntu --bind $PREFIX/etc/resolv.conf:/etc/resolv.conf'" >> ~/.bashrc |
容器化 Ubuntu 的特点:
| 特性 | 说明 |
|---|---|
| 架构 | ARM64(aarch64) |
| 包管理 | apt(完整 Ubuntu 仓库) |
| 无 systemd | 服务需手动启动或通过 profile.d 脚本 |
| root 模拟 | proot 模拟的 root 权限,非真 root |
| 资源损耗 | CPU 约 12-18%,I/O 约 30-50% |
两个关键边界要牢记:没有 systemd(服务启动方式要特别处理)和 root 是模拟的(不能依赖特权操作),这在后续部署中会反复遇到。
2. Ubuntu Server 安装与基础配置
环境:Ubuntu 22.04.5 LTS Server 版 | VMware 虚拟机 | 网卡 ens33 | 静态 IP 192.168.44.10
2.1 系统安装
下载 Ubuntu 22.04.5 LTS Server 镜像,VMware 新建虚拟机(典型配置),预留 2 核 CPU、2G 内存、30G 磁盘。安装关键选项:
- 语言推荐 English(服务器环境避免中文乱码);
- 网络默认 DHCP,装完再改静态 IP;
- 镜像源可替换为
http://mirrors.aliyun.com/ubuntu; - 磁盘分区选 Use an entire disk(如需关闭 LVM 可取消 Set up this disk as an LVM group);
- SSH 服务(必选):勾选 Install OpenSSH server,否则无法远程连接。
2.2 配置静态 IP(Netplan 新版写法)
先禁用 cloud-init 网络管理(否则重启 IP 变回 DHCP):
sudo tee /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg <<-'EOF' |
写入 Netplan 配置,用 routes 替代已废弃的 gateway4:
network: |
安全应用:sudo netplan try(120 秒超时自动回滚,防止 SSH 失联)。验证:ip a、ip route、ping mirrors.aliyun.com。
2.3 精简登录 MOTD 提示
sudo chmod -x /etc/update-motd.d/10-help-text # 文档链接行 |
SSH 客户端显示的「连接主机… 连接主机成功」是本地软件输出,不属于系统 MOTD,服务器端无法关闭。
2.4 更换国内源与常用工具
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak # 备份 |
3. proot 容器服务自启动原理
很多人以为「把服务脚本放进 /etc/profile.d 就能开机自启」,这句话只说对了一半。
3.1 /etc/profile.d 到底何时执行
关键结论:/etc/profile 会加载 /etc/profile.d/*.sh,而 /etc/profile 仅在登录 shell(bash -l)才执行。
| 场景 | 是否执行 /etc/profile.d |
|---|---|
proot-distro run ubuntu bash -l(登录 shell) |
✅ 执行 |
proot-distro run ubuntu bash(普通 shell) |
❌ 不执行 |
| 后台服务、systemd | ❌ 不读取 |
所以把服务脚本丢进 /etc/profile.d/,只能实现「手动进入交互式登录容器时启动」,不能实现后台开机自启。
3.2 交互式登录自动启动的用法
# 写脚本 /etc/profile.d/myservice.sh |
3.3 三个必须避开的坑
proot-distro login ubuntu默认不是登录 shell(不带-l),脚本不会跑;- 多次进入容器会重复执行脚本,需加进程检测:
if ! pgrep -f "nginx" >/dev/null ;then
service nginx start
fi - 这不是真正的开机自启——Termux 重启、容器没运行时不会自动跑;proot Ubuntu 无 systemd。
3.4 三种真正可落地的方案
| 方案 | 触发时机 | 缺点 | 使用条件 |
|---|---|---|---|
| A. /etc/profile.d | 手动登录进入交互式 shell | 不登录不启动;重复执行;不能常驻 | 必须 -- bash -l |
| B. proot-distro 启动钩子 | 容器启动就运行 | — | 修改 /data/data/com.termux/files/usr/etc/proot-distro/ubuntu.sh |
| C. Termux 侧自启动 | Termux 开机先启容器 | proot 会话结束进程销毁 | 需 tmux 套一层常驻 |
方案 C 的 tmux 常驻示例:
tmux new-session -d -s ubuntu_srv "proot-distro login ubuntu -- bash -c 'service nginx start; sleep infinity'" |
结论:profile.d 方案成立的前提是「可执行权限 + bash -l 登录 + 进程检测」,但它不等于开机自启。需要真正常驻时,请用启动钩子或 Termux 侧 tmux。
4. Nginx 网关与站点配置
Nginx 作为统一网关,4 个站点按端口分发,静态托管与反向代理各司其职。
4.1 安装与配置结构
apt update && apt install -y nginx |
主配置末尾 include:/etc/nginx/conf.d/*.conf 和 /etc/nginx/sites-enabled/*。
4.2 站点 1:Hexo 博客(8080)
server { |
4.3 站点 2:图片画廊(8081)
server { |
4.4 站点 3:SearXNG 反向代理(8083)
server { |
proxy_buffering off 是因为 SearXNG 是流式/长轮询接口,关闭缓冲避免结果延迟。
4.5 站点 4:OALD10 词典(8099)
server { |
4.6 sites-available / sites-enabled 机制
sites-available 存所有站点配置,sites-enabled 存实际启用的站点(Nginx 只读此目录),两者靠软链接关联:
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/ # 启用 |
4.7 无 systemd 环境下的 Nginx 管理
nginx # 启动 |
4.8 Nginx 500 报错排错
日志报 rewrite or internal redirection cycle while internally redirecting to "/index.html",三个原因:root 目录配置错误(旧指向 /var/www/hexo-blog,实际 /var/www/blog-nesthub)、容器无法监听 80/443、无 systemctl。解决:修正 root 路径、规范 try_files、index 写在 server 层级,并执行:
chown -R www-data:www-data /var/www/blog-nesthub |
5. Hexo 博客部署与自动化运维
5.1 架构
- 源码仓库:Gitee 私有仓库(分支 main)
- 编译工具链:Node.js + pnpm + Hexo
- 产物目录:
/var/www/blog-nesthub(nginx root) - 多配置编译:
hexo generate --config _config.yml,_config.nesthub.yml双配置合并
5.2 部署流程(deploy-blog.sh,一键 4 步)
cd /root/zzcblog-source |
脚本完整流程:
[1/4] 更新源码:git fetch + checkout + pull(无 stash,保持干净工作区) |
5.3 亮点设计
- 并发锁:
flock锁/tmp/deploy-blog.lock; - 原子发布:先复制到
.new再mv切换,避免半成品页面; - 依赖指纹:md5 判断依赖是否变更;
- DNS 自愈:ping 不通阿里云就重写 resolv.conf(223.5.5.5 / 114.114.114.114);
- 错误捕获:
trap ERR记录失败行号,trap EXIT写结束日志; - 日志:
/var/log/deploy-blog.log。
5.4 原脚本隐患修正
chown -R root:root导致 Nginx(www-data)读权限不足 → 改为chown -R www-data:www-data "$TMP_WEB";- 危险
git stash逻辑 → 改为无 stash 的 fetch+checkout+pull; - 缺并发锁 → 加 flock。
5.5 访问与常见报错
访问 http://blog.nesthub.dpdns.org:8080(无证书,https 暂不可用)。
| 报错 | 原因 | 解决 |
|---|---|---|
bind() to 0.0.0.0:80 failed (13: Permission denied) |
proot 容器限制 | 改用 8080 高端口 |
System has not been booted with systemd |
无 systemd | 用 pkill nginx 管理进程 |
| rewrite 重定向循环 500 | root 路径错误 | 修正 root、规范 try_files |
6. MinIO 对象存储与图床
6.1 安装(ARM64 / 国内镜像)
wget https://dl.minio.org.cn/server/minio/release/linux-arm64/minio -O /usr/local/bin/minio |
6.2 启动参数
export MINIO_ROOT_USER="<用户名>" |
端口:9000 API / 9001 控制台。MINIO_SERVER_URL 必须设为公网域名,否则预签名 URL 指向内网 IP。
6.3 mc 客户端与匿名权限
从 2024 年新版本起,网页端「设置公开访问」按钮被删除,只能用命令行
mc改匿名权限。
curl https://dl.minio.org.cn/client/mc/release/linux-arm64/mc -o /usr/local/bin/mc |
6.4 健康检查
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:9000 # 403 正常(无 bucket 时) |
MinIO 根路径返回 403 是正常行为,不要误判为故障。
6.5 Cloudflare Tunnel + 图床(公网方案)
架构:PicGo → HTTPS → Cloudflare Edge → cloudflared → MinIO(9000/9001)。
安装并创建隧道:
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64 |
配置 ~/.cloudflared/config.yml:
tunnel: <隧道 UUID> |
DNS 加两条 CNAME 指向 .cfargotunnel.com(代理开启橙色云),并给两个域名配置 Bypass cache 缓存规则。
PicGo 接入用专用用户(不要用 root):
mc admin user add myminio picgo <密码> |
PicGo 字段:EndPoint minio.nesthub.dpdns.org(不含 https://)、UseSSL 勾选、AccessKey picgo、Bucket test;S3 插件需勾选 Path style。
6.6 常见坑点速查
| 问题 | 根因 | 解决 |
|---|---|---|
| 控制台资源加载失败 | 9000/9001 分离 | 双子域名 + ingress 分别路由 |
| Tunnel 连不上 | QUIC 被干扰 | protocol: http2 |
| PicGo AccessDenied | 无 s3:PutObject | 附加 readwrite 策略 |
| PicGo SignatureDoesNotMatch | Cloudflare 改写头 | Accept-Encoding: identity |
| Region 不匹配 | 插件硬编码 us-east-1 | MINIO_REGION_NAME="us-east-1" |
| 图片直链 403 | 匿名无读取权限 | mc anonymous set download |
7. SearXNG 元搜索引擎
7.1 源码 + venv 安装
git clone https://github.com/searxng/searxng.git /root/searxng |
运行入口 /root/searxng/venv/bin/python -m searx.webapp,配置文件 /etc/searxng/settings.yml。
7.2 关键配置
server: |
7.3 启动脚本与 Nginx 反代
/root/scripts/searxng.sh 支持 start|stop|restart|status,环境变量 SEARXNG_SETTINGS_PATH、PYTHONPATH=/root/searxng,PID /root/scripts/pids/searxng.pid,日志 /root/scripts/logs/searxng.log。
Nginx 反代(8083 → 127.0.0.1:9090)见第四章 4.4 节,核心是 proxy_buffering off。
8. OALD10 网页词典
基于牛津高阶英汉双解词典第 10 版(OALD10)的自建查词服务,零依赖纯标准库实现,适配 Yomitan 样式。
8.1 目录与数据
/root/oald10/ |
8.2 后端接口(server.py,监听 12700)
| 接口 | 说明 |
|---|---|
GET /api/lookup?w=WORD |
精确查词(重定向/多词性聚合/拼写建议) |
GET /api/search?q=TEXT&n=20 |
全文搜索(英文 FTS5 / 中文 LIKE,offset 分页) |
GET /api/prefix?p=PREF&n=30 |
词头前缀匹配 |
GET /api/random |
随机返回一个主词条 |
启动:python3 server.py --host 127.0.0.1 --port 12700。
8.3 Nginx 安全反代
安全设计(配置见 4.5 节):静态资源 nginx 直服、/api/* 反代到 12700、默认 location / { return 404; } 堵死源码/数据库下载路径、yomitan-css 正则白名单。
9. Alist 文件云盘
9.1 安装
mkdir -p /opt/alist |
数据目录 /opt/alist/data/(config.json + data.db + log),端口 HTTP 5244 / FTP 5246。
9.2 配置与启动
{ |
# /root/scripts/start_alist.sh |
脚本依赖 PATH 里的
alist命令,也可用绝对路径/opt/alist/alist server。默认仅局域网访问。
10. 内网穿透与远程访问
10.1 frp(博客域名 → 8080)
架构:浏览器 → blog.nesthub.dpdns.org → frp 服务端 → frpc → 127.0.0.1:8080 → nginx → Hexo。
隧道配置 /root/frp/nginx.ini:
[common] |
启动与停止:
nohup /root/frp/frpc -c /root/frp/nginx.ini >> /var/log/frpc-nginx.log 2>&1 & |
经验:日志持续报
connection refused,说明 frpc 活着但后端端口没监听,要 curl 检查后端。
10.2 cloudflared(图床 → Cloudflare CDN)
命名隧道配置 /root/scripts/tunnel-named.yml:
tunnel: <隧道 UUID> |
启动(关键参数):
cloudflared tunnel --config /root/scripts/tunnel-named.yml \ |
10.3 Tailscale(私有组网 + 远程管理)
proot 容器无法创建 TUN 设备,必须用 userspace 模式:
curl -fsSL https://tailscale.com/install.sh | sh |
userspace 模式需用 tailscale serve 显式转发端口:
tailscale --socket=/run/tailscale/tailscaled.sock serve tcp:8080:localhost:8080 |
更推荐:Android 宿主直接装 Tailscale App(TUN 模式),有虚拟网卡、自动路由、内核态性能,容器内监听 0.0.0.0 的服务自动暴露。
Windows 同时跑 FlClash + Tailscale,需在 FlClash TUN 排除 Tailscale 网段:
tun: |
Tailscale 快的原因:WireGuard 加密 + 设备间 P2P 直连(Direct 直连延迟最低、吞吐最高;DERP 中继国内较慢)。提速方案:路由器放行 UDP 41641、自建 DERP 中继、配置 Peer Relay。
10.4 三种方案定位
| 方案 | 适用场景 | 公网域名 |
|---|---|---|
| frp | 博客等 HTTP 服务对外 | blog → 8080 |
| cloudflared | 图床走 CDN | img → 9000 |
| Tailscale | 私有组网、远程管理 | 无公网域名,P2P |
11. mihomo 科学上网代理
11.1 端口配置
| 项 | 值 |
|---|---|
| mixed-port | 7890(HTTP/SOCKS5 混合) |
| allow-lan | true |
| mode | rule |
| external-controller | 0.0.0.0:9097(避开 SearXNG 的 9090) |
| external-ui | /etc/mihomo/ui |
| DNS listen | 127.0.0.1:1053(fake-ip) |
11.2 终端代理开关(proxy.sh)
由 .bashrc source 引入,用函数改当前终端环境变量:
proxy on / off / status |
no_proxy 配置:
PROXY_NO_PROXY="localhost,127.0.0.1,::1,192.168.5.85,nesthub.dpdns.org" |
⚠️ curl 的 no_proxy 不支持
*.domain通配,裸域名已匹配所有子域名,不要加星号。
11.3 订阅自动更新
每天 04:40 cron 触发:curl 下载订阅(UA 伪装 clash.meta)→ 校验含 proxies: → sed 注入 external-controller 9097 等修正 → mihomo -d /etc/mihomo -t 语法校验 → 通过才覆盖并重启。日志 /var/log/mihomo/sub-update.log。
12. MySQL / MariaDB 数据库
12.1 安装与启动(绕过 systemd)
apt install -y mariadb-server # 推荐 MariaDB(约 200-250MB 内存) |
12.2 安全初始化与建库
mysql_secure_installation # 设 root 密码、移除匿名用户、禁远程 root、删 test 库 |
CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; |
12.3 远程访问与自启
编辑 50-server.cnf 设 bind-address = 0.0.0.0,重启后 mysql -u myuser -p -h 192.168.x.x 测试。
cat > /etc/profile.d/mysql.sh <<'EOF' |
12.4 现实约束
| 项目 | 说明 |
|---|---|
| 内存占用 | MariaDB ~200-250MB,MySQL ~350-400MB |
| 性能 | proot I/O 开销大,不适合高并发 |
| 保活 | 锁屏可能被杀,需 termux-wake-lock |
| 适用场景 | 开发测试、小型应用后端,不推荐生产 |
13. 服务管理脚本体系与故障排查
13.1 脚本总目录
所有脚本集中在 /root/scripts/,是这套服务器的运维入口:
| 脚本 | 作用 |
|---|---|
manage-services.sh |
统一管理 nginx + MinIO + frpc |
start-all.sh |
一键启动 nginx + MinIO + SearXNG |
start-minio.sh |
MinIO 管理(start/stop/restart/status/logs) |
searxng.sh / mihomo.sh |
各自服务管理 |
proxy.sh |
终端代理开关 |
update-mihomo-sub.sh |
订阅自动更新 |
start_alist.sh / start-oald10.sh |
服务启动 |
deploy-blog.sh / git_pull.sh |
博客部署 |
cf-auto-run.sh / cf-named-start.sh / cf-stop.sh |
cloudflared 管理 |
stop-frpc.sh |
停止 frpc 实例 |
13.2 统一管理脚本(manage-services.sh)
INSTANCES=("nginx") 数组声明 frpc 实例,扩展新隧道加元素即可。start_frpc_instance() 检查配置 → 防重复 → nohup 启动 → 写 pid → 验证存活;stop_frpc_instance() 读 pid → kill → kill -9 兜底 → 清理 pid。
13.3 通用服务脚本模板
任何需手动启动的服务都能套用(set -euo pipefail + 五子命令 + 防重复 + 优雅停止 + 彩色日志)。极简版:
|
13.4 PID / 日志约定
统一路径避免混乱:MinIO /var/run/minio.pid、frpc /var/run/frpc-<name>.pid、SearXNG /root/scripts/pids/searxng.pid、mihomo /root/scripts/pids/mihomo.pid、cloudflared /root/scripts/pids/cf-tunnel.pid、OALD10 /root/oald10/server.pid。
13.5 定时任务(cron)
# 每天 05:38 acme.sh 证书续期 |
13.6 运维速查
健康检查统一用 curl + pgrep(proot 下 ss/netstat/lsof 不可用):
curl -s -o /dev/null -w "nginx8080=%{http_code}\n" http://127.0.0.1:8080 |
13.7 故障排查经验集
① 僵尸在线:pgrep -x minio 有 PID 但端口全拒、日志 0 字节。排查 ps aux | grep minio 看 STAT 是否 S/D、数 socket fd、curl 实测端口。根因是进程异常沉睡未完成端口绑定,start-minio.sh restart 解决。教训:进程存在 ≠ 服务正常,必须用 curl 做健康检查。
② frpc 隧道不通:依次查进程 → 日志 → 后端端口 curl → pid 文件是否残留。常见根因:后端没起来、pid 残留导致脚本跳过启动、域名配置不对。
③ 端口探测手法:curl -s -o /dev/null --max-time 2 -w "%{http_code}" http://127.0.0.1:PORT + pgrep -x 服务名。
④ no_proxy 通配坑:不支持 *.domain,裸域名已匹配子域名,不要加星号。
⑤ 端口冲突:mihomo external-controller 从 9090 改 9097 避开 SearXNG,新增服务先查端口表。
⑥ MinIO 预签名 URL 指向内网:必须设 MINIO_SERVER_URL 为公网域名。
13.8 端口规划总表
| 端口 | 服务 | 绑定 | 用途 |
|---|---|---|---|
| 8080 | nginx | 0.0.0.0 | Hexo 博客 |
| 8081 | nginx | 0.0.0.0 | 图片画廊 |
| 8083 | nginx | 0.0.0.0 | SearXNG 反代 |
| 8099 | nginx | 0.0.0.0 | OALD10 词典 |
| 9090 | SearXNG | 127.0.0.1 | 搜索引擎本体 |
| 12700 | OALD10 | 127.0.0.1 | 词典 API |
| 5244/5246 | Alist | 0.0.0.0 | 网盘 HTTP/FTP |
| 9000/9001 | MinIO | 0.0.0.0 | API / 控制台 |
| 7890 | mihomo | 0.0.0.0 | 混合代理 |
| 9097 | mihomo | 0.0.0.0 | Web 面板 |
| 1053 | mihomo | 127.0.0.1 | DNS |
| 7000 | frpc | - | 连接 frp 服务端 |
结语
一台手机从「能跑 Linux」到「承载十几个服务的家庭服务器」,关键不在某个单独的工具,而在一套连贯的方法:用 Termux + proot 搭出环境,用 Nginx 做统一网关,用脚本体系替代 systemd 做服务管理,用 frp/cloudflared/Tailscale 解决公网访问,最后用「进程活着 ≠ 服务正常」这类经验守住稳定性。本文覆盖的每一条命令和配置都来自实际运行环境,可直接对应真实文件,供按需取用和二次修改。
本文基于 Termux + proot-distro Ubuntu 环境整理,源文件中的明文密码、token、secret、隧道 UUID 等凭据已脱敏为占位符。



