Android + chroot Ubuntu 22.04 ARM64 服务部署笔记合集

环境说明:红米 Note 12 Turbo,已 root(Magisk),16G + 1T,正常运行;通过 chroot 运行 ubuntu-base-22.04.5-base-arm64.tar.gz。
统一约定:chroot 内无 systemd,服务统一使用 SysV init.d 脚本或 nohup 后台启动,并全部接入 /opt/start_all.sh 做保活;开机由 Magisk 脚本拉起 chroot。


目录


一、基础环境与开机自启框架

返回目录

1.1 环境与路径约定

项目 约定值
设备 红米 Note 12 Turbo(已 root / Magisk,16G + 1T)
chroot 目录 /data/linux
发行版 Ubuntu 22.04.5 base arm64(无 systemd)
局域网 IP 192.168.5.14
服务管理 SysV init.d / nohup + 保活脚本
统一启动脚本 /opt/start_all.sh

各服务端口一览:

服务 端口 说明
sshd 22 远程登录
nginx 80 博客静态站
SILO 9000 / 9001 S3 API / Web 控制台
AList 5244 网盘聚合(WebDAV 走 /dav)
mihomo 7890 / 9090 代理端口 / 控制面板
frpc 由面板分配 内网穿透隧道

1.2 Magisk 开机脚本 chroot_server.sh

宿主机(Android)侧,Magisk 开机脚本位于 /data/adb/service.d/chroot_server.sh:负责挂载必要目录、写入 DNS、拉起 chroot 内的 /opt/start_all.sh。

cat > /data/adb/service.d/chroot_server.sh << 'EOF'
#!/system/bin/sh

sleep 30

CHROOT_DIR="/data/linux"

if mountpoint -q "$CHROOT_DIR/proc"; then
exit 0
fi

# Global wake lock - keep phone awake for all services
echo "chroot_wakelock" > /sys/power/wake_lock
echo "N" > /sys/module/wlan/parameters/power_save 2>/dev/null

mount --bind /dev "$CHROOT_DIR/dev" 2>/dev/null
mount --bind /proc "$CHROOT_DIR/proc" 2>/dev/null
mount --bind /sys "$CHROOT_DIR/sys" 2>/dev/null
mount -t devpts -o gid=5,mode=0620 devpts "$CHROOT_DIR/dev/pts" 2>/dev/null
mount -t tmpfs -o mode=1777 tmpfs "$CHROOT_DIR/tmp" 2>/dev/null
mount -t tmpfs -o mode=0755 tmpfs "$CHROOT_DIR/run" 2>/dev/null

if [ -L "$CHROOT_DIR/etc/resolv.conf" ] || [ ! -f "$CHROOT_DIR/etc/resolv.conf" ]; then
rm -f "$CHROOT_DIR/etc/resolv.conf"
echo "nameserver 8.8.8.8" > "$CHROOT_DIR/etc/resolv.conf"
echo "nameserver 114.114.114.114" >> "$CHROOT_DIR/etc/resolv.conf"
fi

chroot "$CHROOT_DIR" /opt/start_all.sh &
EOF
chmod 755 /data/adb/service.d/chroot_server.sh

说明:仅挂载必要目录并调用 /opt/start_all.sh,本身保持简单、不阻塞 Magisk 启动流程。

1.3 chroot 统一启动脚本 start_all.sh

chroot 内(先 chroot /data/linux /bin/bash)的 /opt/start_all.sh 负责启动所有服务并进入保活循环。

cat > /opt/start_all.sh << 'EOF'
#!/bin/bash
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
unset TMPDIR TMP

mkdir -p /run/sshd /tmp
chmod 755 /run/sshd
chmod 1777 /tmp

# 启动所有服务
/usr/sbin/sshd
/usr/sbin/nginx
/etc/init.d/silo start
/etc/init.d/alist start

# 如果需要启动 Node.js 应用,在这里添加:
# cd /opt/myapp && node app.js >> /var/log/myapp.log 2>&1 &

# 等待所有服务就绪
sleep 3

# 二次确认 AList 端口(最多等6秒)
for i in 1 2 3; do
if ss -tlnp | grep -q ':5244'; then
break
fi
sleep 2
done

# 保活循环(每5分钟检查一次,后台运行)
(
while true; do
sleep 300
pgrep -x sshd || /usr/sbin/sshd
pgrep -x nginx || /usr/sbin/nginx
pgrep -f '/usr/local/bin/silo' || /etc/init.d/silo start
pgrep -f '/opt/alist/alist server' || /etc/init.d/alist start
done
) &
exit 0
EOF
chmod 755 /opt/start_all.sh

要点:

  • 已去掉 exec /bin/bash,改为 exit 0,避免阻塞 Magisk 脚本。
  • 增加了 AList 端口二次确认循环(最多等待 6 秒),解决开机时 AList 启动慢的问题。
  • 保活循环在后台运行,不影响脚本退出;保活判断用全路径匹配,避免与其他同名进程混淆。
  • 预留了 Node.js 应用启动注释行,按需取消注释。
  • 后续各服务(如 frpc)的启动与保活统一在此脚本中追加,见 第八章。

1.4 端口与验证

执行完上述两个脚本后,重启手机或手动运行 sh /data/adb/service.d/chroot_server.sh 测试,然后验证端口:

ss -lntp | grep -E ':22|:80|:9000|:9001|:5244'

二、Nginx 安装与博客静态站部署

返回目录

博客静态文件位于 /var/www/zzcblog/(来自 git clone https://github.com/zzc-blog/zzc-blog.github.io.git)。以下为从零开始的完整部署流程(已进入 chroot 并拥有 root 权限)。

2.1 安装 Nginx

apt update
apt install -y nginx

安装完成后,Nginx 会自动注册为 sysv 服务(/etc/init.d/nginx),可直接用 service nginx start 启动。

2.2 创建网站根目录并克隆博客仓库

mkdir -p /var/www
cd /var/www
git clone https://github.com/zzc-blog/zzc-blog.github.io.git zzcblog

此时博客文件位于 /var/www/zzcblog/,包含 index.html、css/、js/、posts/ 等目录。

2.3 设置目录权限

chown -R www-data:www-data /var/www/zzcblog
chmod -R 755 /var/www/zzcblog

2.4 配置 Nginx 站点

创建站点配置文件:

cat > /etc/nginx/sites-available/zzcblog << 'EOF'
server {
listen 80;
listen [::]:80;
root /var/www/zzcblog;
index index.html;
server_name _;

location / {
try_files $uri $uri/ =404;
}

# 静态资源缓存
location ~* \.(html|xml|json)$ {
expires 10m;
}
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|webp|woff2?|ttf|eot)$ {
expires 7d;
access_log off;
}

gzip on;
gzip_types text/html text/css application/javascript application/json image/svg+xml;
}
EOF

启用站点并禁用默认站点(避免冲突):

ln -s /etc/nginx/sites-available/zzcblog /etc/nginx/sites-enabled/zzcblog
rm -f /etc/nginx/sites-enabled/default

2.5 测试配置并重载

nginx -t                    # 测试语法
service nginx reload # 重载配置(无 systemd 用 service)

nginx -t 输出 syntax is ok 和 test is successful 即配置无误。

2.6 验证访问

浏览器打开 http://192.168.5.14(或你的手机局域网 IP)应看到博客首页;也可在 chroot 内测试:

curl -s http://127.0.0.1 | head -c 200

应返回包含 <html> 的页面内容。

2.7 后续更新博客

GitHub 仓库推送新文章后,在 chroot 内执行:

cd /var/www/zzcblog
git pull
service nginx reload

也可保存为 /opt/update_zzcblog.sh 并赋予执行权限,方便一键更新。

2.8 (可选)绑定域名 + HTTPS

域名 A 记录指向手机公网 IP 后,在 chroot 内安装 certbot:

apt install -y certbot python3-certbot-nginx
certbot --nginx -d yourdomain.com

Certbot 会自动修改 Nginx 配置并启用 HTTPS。


三、Node.js 安装配置

返回目录

Node.js 官方提供 arm64 预编译二进制,解压到 /opt/node 并软链到 /usr/local/bin 是最干净的做法,升级只需换目录。

3.1 进 chroot 并确认架构

chroot /data/linux /bin/bash
uname -m # 必须返回 aarch64

3.2 下载 Node.js arm64 二进制

去 Node.js 官网 或国内镜像获取 LTS 版。以 v20.18.0 为例(可替换为 v22.x 或更高版本):

cd /tmp
VER=v20.18.0
wget -O node.tar.xz "https://nodejs.org/dist/${VER}/node-${VER}-linux-arm64.tar.xz"
# 国内镜像(更快):
# wget -O node.tar.xz "https://npmmirror.com/mirrors/node/${VER}/node-${VER}-linux-arm64.tar.xz"

解压到 /opt:

tar -xf node.tar.xz -C /opt/
mv /opt/node-${VER}-linux-arm64 /opt/node

3.3 创建软链接到 PATH

ln -sf /opt/node/bin/node /usr/local/bin/node
ln -sf /opt/node/bin/npm /usr/local/bin/npm
ln -sf /opt/node/bin/npx /usr/local/bin/npx
hash -r
node --version # 预期 v20.18.0
npm --version # 预期 10.x

3.4 配置 npm 镜像(可选)

npm config set registry https://registry.npmmirror.com
npm config get registry

3.5 全局包安装目录

默认全局包安装在 /opt/node/lib/node_modules,无需额外配置;软链已包含 /opt/node/bin,全局包安装后命令自动可用。

npm install -g yarn pm2   # 如果需要
yarn --version
pm2 --version

注意:pm2 用于进程管理,但在 chroot 无 systemd 环境下,pm2 的 startup 功能不能用 systemd 自动启动,需要手动加入 /opt/start_all.sh 保活。

3.6 验证 Node.js 正常运行

cat > /tmp/test.js << 'EOF'
const http = require('http');
const server = http.createServer((req, res) => {
res.end('Hello from Node.js!');
});
server.listen(3000, () => console.log('Node test on :3000'));
EOF
node /tmp/test.js &
sleep 1
curl http://127.0.0.1:3000
kill %1

应输出 Hello from Node.js!。

3.7 集成到开机自启

假设 Node.js 应用位于 /opt/myapp/app.js:

方法一:直接添加到 /opt/start_all.sh

cat >> /opt/start_all.sh << 'EOF'

# Node.js app
cd /opt/myapp
node app.js >> /var/log/myapp.log 2>&1 &
EOF

方法二:使用 pm2 管理(推荐,自带保活)

npm install -g pm2
pm2 start /opt/myapp/app.js --name myapp
pm2 save
# 将 pm2 的 resurrect 加入 start_all.sh
echo "pm2 resurrect" >> /opt/start_all.sh

pm2 的 resurrect 会读取 /root/.pm2/dump.pm2 恢复所有进程,无需 systemd。

3.8 升级 Node.js

cd /tmp
NEW_VER=v22.11.0
wget https://nodejs.org/dist/${NEW_VER}/node-${NEW_VER}-linux-arm64.tar.xz
tar -xf node-${NEW_VER}-linux-arm64.tar.xz -C /opt/
rm /opt/node # 删除旧符号链接或目录
mv /opt/node-${NEW_VER}-linux-arm64 /opt/node
node --version

3.9 排错

  • node: command not found:检查 /usr/local/bin/node 是否存在,ls -l /usr/local/bin/node 是否指向正确的 /opt/node/bin/node。
  • libstdc++.so.6: version GLIBCXX_3.4.29 not found:Ubuntu 22.04 自带 libstdc++6 较新,一般没问题;若出现执行 apt install -y libstdc++6。
  • npm 安装包报权限错误:全局安装时不要用 sudo(chroot 内已是 root);若使用非 root 用户,需 npm config set prefix /home/user/.npm-global 并调整 PATH。
  • pm2 启动后进程消失:检查 pm2 logs,可能是应用代码问题。

3.10 路径总结

组件 路径 说明
Node.js 主程序 /opt/node/bin/node 二进制
npm /opt/node/bin/npm 包管理器
全局包 /opt/node/lib/node_modules 全局安装位置
软链 /usr/local/bin/{node,npm,npx} 可直接命令行调用

四、SILO 对象存储安装配置

返回目录

环境约定:chroot(Ubuntu 22.04 ARM64,无 systemd)+ SILO(PGSTY SILO,MinIO 社区分支)+ 9000 S3 / 9001 控制台 + 数据 /data/silo + 账号 zzc + init.d 自启 + 日志 /var/log/silo.log。

SILO 是 S3 兼容对象存储,arm64 有官方二进制;9000 是 API、9001 是 Web 控制台,用 MINIO_ROOT_USER/MINIO_ROOT_PASSWORD 和 server /data --console-address :9001 启动。

如果你 /usr/local/bin/silo 其实不是 PGSTY SILO,而是别的自研二进制,把「下载/启动参数」那段替换成你自己的即可,init.d 模板通用。

4.1 进 chroot、确认架构

chroot /data/linux /bin/bash
uname -m # 期望 aarch64
silo --version 2>/dev/null || echo "尚未安装silo"

已有旧 silo 想重装先停掉:/etc/init.d/silo stop。

4.2 下载 SILO arm64 二进制

PGSTY 提供 arm64 二进制/容器/DEB/RPM;chroot 无 systemd,用二进制最干净。

cd /tmp
REL=RELEASE.2026-09-16T00-00-00Z # 按官网当前版替换
wget -O silo_arm64.tar.gz "https://github.com/pgsty/silo/releases/download/${REL}/silo_${REL}_linux_arm64.tar.gz"
tar -xzf silo_arm64.tar.gz
install -m 0755 silo /usr/local/bin/silo
hash -r
silo --version

如果官网只给 minio 命名的历史包而你要叫 silo,直接把二进制命名成 silo 放 /usr/local/bin 也行,启动参数完全一致。

4.3 数据目录与运行账号

mkdir -p /data/silo
touch /var/log/silo.log
chmod 755 /data/silo
chmod 644 /var/log/silo.log

groupadd -r silo 2>/dev/null
useradd -M -r -g silo silo 2>/dev/null
chown -R silo:silo /data/silo /var/log/silo.log

保活脚本里用 root 启更简单;用 silo 用户启要把 /data/silo 属主给它。

4.4 启动参数 / 环境文件

单机单盘最简。建 /etc/default/silo:

cat > /etc/default/silo << 'EOF'
# SILO 单机单目录配置
SILO_BIN=/usr/local/bin/silo
SILO_DATA=/data/silo
SILO_OPTS="--address :9000 --console-address :9001"
# 管理员账号(S3 root)
MINIO_ROOT_USER=zzc
MINIO_ROOT_PASSWORD=ZZC20010102
# 日志
SILO_LOG=/var/log/silo.log
# 运行用户;留空或 root 表示直接 root 跑
SILO_USER=silo
EOF

注意:SILO/MinIO 要求 root 密码至少 8 位,ZZC20010102 满足;生产建议改更长随机串。

手动先试跑(排错用):

MINIO_ROOT_USER=zzc MINIO_ROOT_PASSWORD=ZZC20010102 \
/usr/local/bin/silo server /data/silo --address :9000 --console-address :9001 2>&1 | tee /var/log/silo.log

浏览器开 http://192.168.5.14:9001 用 zzc / ZZC20010102 登录;OK 后 Ctrl-C,改走 init.d。

4.5 /etc/init.d/silo(无 systemd)

cat > /etc/init.d/silo << 'EOF'
#!/bin/sh
### BEGIN INIT INFO
# Provides: silo
# Required-Start: $local_fs $network $named
# Required-Stop: $local_fs $network $named
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: SILO S3 object storage (9000 API / 9001 console)
### END INIT INFO

NAME=silo
BIN=/usr/local/bin/silo
DATA=/data/silo
PIDFILE=/var/run/$NAME.pid
LOGFILE=/var/log/$NAME.log
USER=silo
ADDRESS=:9000
CONSOLE=:9001
ROOT_USER=zzc
ROOT_PASS=ZZC20010102

. /lib/lsb/init-functions

start() {
if [ -f "$PIDFILE" ] && kill -0 "$(cat $PIDFILE)" 2>/dev/null; then
log_success_msg "$NAME already running"
return 0
fi
log_daemon_msg "Starting $NAME"
start-stop-daemon --start --background \
--make-pidfile --pidfile "$PIDFILE" \
--chuid "$USER" \
--exec "$BIN" -- \
server "$DATA" --address "$ADDRESS" --console-address "$CONSOLE" \
>>"$LOGFILE" 2>&1
# 通过环境变量传管理员账号
export MINIO_ROOT_USER=$ROOT_USER
export MINIO_ROOT_PASSWORD=$ROOT_PASS
log_end_msg $?
}

stop() {
if [ ! -f "$PIDFILE" ] || ! kill -0 "$(cat $PIDFILE)" 2>/dev/null; then
log_success_msg "$NAME not running"
return 0
fi
log_daemon_msg "Stopping $NAME"
start-stop-daemon --stop --pidfile "$PIDFILE" --retry 10
rm -f "$PIDFILE"
log_end_msg $?
}

status() {
if [ -f "$PIDFILE" ] && kill -0 "$(cat $PIDFILE)" 2>/dev/null; then
echo "$NAME running (pid $(cat $PIDFILE))"
return 0
fi
echo "$NAME not running"
return 1
}

case "$1" in
start) start ;;
stop) stop ;;
restart) stop; sleep 1; start ;;
status) status ;;
*) echo "Usage: /etc/init.d/silo {start|stop|restart|status}"; exit 1 ;;
esac
EOF
chmod 755 /etc/init.d/silo

小坑:start-stop-daemon --exec 后面跟参数时,环境变量要在它之前 export 才进子进程。若你用 root 跑,把 USER=root、去掉 --chuid 即可。

启用开机(无 systemd 用 update-rc.d)并测试:

update-rc.d silo defaults 99
/etc/init.d/silo start
sleep 3
ss -lntp | grep -E ':9000|:9001'
tail -n 20 /var/log/silo.log

4.6 接入 /opt/start_all.sh 保活

把 start_all 里 silo 行统一成 init.d:

/etc/init.d/silo start

保活循环用进程名判断(SILO 进程名是 silo):

pgrep -x silo || /etc/init.d/silo start

4.7 建桶 / 客户端验证(mcli)

SILO 兼容 mcli(MinIO client)。

cd /tmp
wget -O mcli https://github.com/pgsty/silo/releases/download/${REL}/mcli_${REL}_linux_arm64 # 以官网路径为准
install -m 0755 mcli /usr/local/bin/mcli

mcli alias set silo http://127.0.0.1:9000 zzc ZZC20010102
mcli admin info silo
mcli mb silo/blog # 建个桶给静态资源/图床
mcli cp /var/www/zzcblog/img/logo.png silo/blog/

Web 控制台 http://192.168.5.14:9001 → Buckets → 建桶、设 public/私有权限、拿预签名 URL。

4.8 Nginx 反代(可选,走域名/HTTPS)

如果外部用 s3.你的域名 和 silo.你的域名:

cat > /etc/nginx/sites-available/silo << 'EOF'
server {
listen 80;
server_name s3.example.com;
location / {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
server {
listen 80;
server_name silo.example.com;
location / {
proxy_pass http://127.0.0.1:9001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
EOF
ln -s /etc/nginx/sites-available/silo /etc/nginx/sites-enabled/silo
nginx -t && service nginx reload

要 HTTPS 再跑 certbot --nginx -d s3.example.com -d silo.example.com。

4.9 常见排错

  • 9000/9001 只监听 127.0.0.1:检查是否加了 --address 127.0.0.1:9000;要局域网访问用 :9000、:9001 全网卡。
  • 日志报 Invalid Argument / 时间相关签名失败:chroot 内装 ntp 或确保手机系统时间准;S3 签名对时钟敏感。
  • 密码太短起不来:root 密码至少 8 位。
  • init.d start 后立刻退出:tail /var/log/silo.log;多为 /data/silo 权限或 USER 不存在,用 root 跑可先排除权限问题。
  • 与旧 minio 数据共存:SILO 保留 .minio.sys 格式,旧 minio 数据目录可直接当 /data/silo 挂;但版本跨度大先备份。

五、AList 安装配置

返回目录

环境约定:chroot Ubuntu 22.04 ARM64、无 systemd、/opt/alist、监听 5244、日志 /var/log/alist.log、init.d 自启、接入 /opt/start_all.sh 保活。

AList 官方手动安装就是下载对应架构二进制、alist server 默认起在 0.0.0.0:5244,首次运行打印初始 admin 密码;v3.25+ 密码存 hash,用 alist admin random/set 管理。

5.1 进 chroot、确认架构与依赖

chroot /data/linux /bin/bash
uname -m # 期望 aarch64
ldd --version # 看 glibc;Ubuntu22.04 一般 2.35,正常用 glibc 版
which curl wget tar # 没有就 apt install -y curl wget tar ca-certificates

arm64 常规选 alist-linux-arm64;如果报 GLIBC_2.28 not found 再换 alist-linux-musl-arm64。

5.2 下载安装到 /opt/alist

去 GitHub Releases 取最新版,用变量替版本号:

cd /tmp
VER=v3.42.0 # 按官网当前最新改
wget -O alist.tar.gz "https://github.com/AlistGo/alist/releases/download/${VER}/alist-linux-arm64.tar.gz"
mkdir -p /opt/alist
tar -xzf alist.tar.gz -C /opt/alist
chmod +x /opt/alist/alist
/opt/alist/alist --version

如果 glibc 报错:

wget -O alist.tar.gz "https://github.com/AlistGo/alist/releases/download/${VER}/alist-linux-musl-arm64.tar.gz"
tar -xzf alist.tar.gz -C /opt/alist && chmod +x /opt/alist/alist

数据目录默认 /opt/alist/data(config.json、data.db、logs 都在里面),不用额外建。

5.3 首次启动、拿/改管理员密码

前台试跑,确认 5244 能起:

cd /opt/alist
./alist server

看到 start server @ 0.0.0.0:5244 即成功,首次会打印用户名 admin 与初始随机密码(重装会变)。浏览器开 http://192.168.5.14:5244 能进登录页后 Ctrl-C。

改密码(v3.25+):

cd /opt/alist
./alist admin random # 随机重置 admin 密码,终端回显新密码
./alist admin set 新密码 # 手动设新密码

低于 v3.25 用 ./alist admin 查看。

5.4 /etc/init.d/alist(无 systemd)

cat > /etc/init.d/alist << 'EOF'
#!/bin/sh
### BEGIN INIT INFO
# Provides: alist
# Required-Start: $local_fs $network $named
# Required-Stop: $local_fs $network $named
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: AList netdisk aggregator on :5244
### END INIT INFO

NAME=alist
BIN=/opt/alist/alist
WORK=/opt/alist
PIDFILE=/var/run/alist.pid
LOGFILE=/var/log/alist.log
CONF=$WORK/data/config.json

. /lib/lsb/init-functions

start() {
if [ -f "$PIDFILE" ] && kill -0 "$(cat $PIDFILE)" 2>/dev/null; then
log_success_msg "$NAME already running"
return 0
fi
log_daemon_msg "Starting $NAME"
cd "$WORK" || exit 1
start-stop-daemon --start --background \
--make-pidfile --pidfile "$PIDFILE" \
--chdir "$WORK" \
--exec "$BIN" -- server
log_end_msg $?
}

stop() {
if [ ! -f "$PIDFILE" ] || ! kill -0 "$(cat $PIDFILE)" 2>/dev/null; then
log_success_msg "$NAME not running"
return 0
fi
log_daemon_msg "Stopping $NAME"
start-stop-daemon --stop --pidfile "$PIDFILE" --retry 10
rm -f "$PIDFILE"
log_end_msg $?
}

status() {
if [ -f "$PIDFILE" ] && kill -0 "$(cat $PIDFILE)" 2>/dev/null; then
echo "$NAME running (pid $(cat $PIDFILE))"
return 0
fi
echo "$NAME not running"
return 1
}

case "$1" in
start) start ;;
stop) stop ;;
restart) stop; sleep 1; start ;;
status) status ;;
*) echo "Usage: /etc/init.d/alist {start|stop|restart|status}"; exit 1 ;;
esac
EOF
chmod 755 /etc/init.d/alist

update-rc.d alist defaults 99
/etc/init.d/alist start
sleep 3
ss -lntp | grep 5244
tail -n 20 /var/log/alist.log

备注:默认不传 -conf 会用 data/config.json;要显式指定就把 Exec 改成 "$BIN" server -conf data/config.json。

5.5 接入 /opt/start_all.sh 保活

start_all 里把原来 alist 行统一成:

/etc/init.d/alist start

保活循环用全路径匹配,避免和别的 alist 混淆:

pgrep -f '/opt/alist/alist server' || /etc/init.d/alist start

Magisk 开机脚本继续只调 /opt/start_all.sh 即可,不用改。

5.6 基础配置(Web)

登录 http://192.168.5.14:5244,管理员后台 http://192.168.5.14:5244/@manage:

  • 存储 → 添加:本地存储根目录可填 chroot 内路径,如 /var/www/zzcblog 或 /data/silo 导出目录;S3/SILO 选 S3 驱动,endpoint http://127.0.0.1:9000,access/secret 用 silo 的 zzc 账号。
  • 设置 → 站点:改标题、关闭注册、设根目录、JWT 过期时间。
  • 设置 → 其他/离线下载:见下一节。
  • WebDAV:默认随 5244 提供,路径 /dav,用 admin 账号鉴权。

5.7 消除 qBittorrent / Transmission / aria2 三条 WARN

AList 启动报这三条,通常是因为代码里内置了离线下载器初始化、但本机没装对应客户端。不装就不用管;想彻底不提示/不初始化:

  1. 不安装 aria2、qbittorrent、transmission(已经没装就跳过)。
  2. 后台 → 管理 → 设置 → 其他:
    • Aria2 RPC 留空(不填 http://127.0.0.1:6800/jsonrpc)
    • Qbittorrent url 留空(默认预设是 http://admin:adminadmin@localhost:8080/,不配即不连)
    • Transmission 相关 RPC/临时目录留空
    • 离线临时目录(aria2/qbittorrent)留空
  3. 只用 SimpleHttp 离线(本机直链拉取)或不开放离线功能:文件列表右下角离线按钮可不用;若不想任何离线,不挂离线存储即可。
  4. 若日志仍 WARN,确认是「未找到客户端」类,可忽略;只在不影响 5244 监听的情况下不处理。

以后真要离线再装 aria2:

apt install -y aria2
mkdir -p /opt/alist/data/temp/aria2
# aria2c --enable-rpc --rpc-listen-all --rpc-secret=xxx --dir=/opt/alist/data/temp/aria2

再在 AList 其他设置填 Aria2 RPC 即可。

5.8 Nginx 反代 + HTTPS(可选)

cat > /etc/nginx/sites-available/alist << 'EOF'
server {
listen 80;
server_name alist.example.com;

client_max_body_size 0; # 大文件上传不限制,按需要改
location / {
proxy_pass http://127.0.0.1:5244;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /dav {
proxy_pass http://127.0.0.1:5244/dav;
proxy_set_header Host $host;
}
}
EOF
ln -s /etc/nginx/sites-available/alist /etc/nginx/sites-enabled/alist
nginx -t && service nginx reload

要 HTTPS:certbot --nginx -d alist.example.com,并把 AList 后台「站点 URL」改成 https。

5.9 排错

  • 5244 起不来:tail /var/log/alist.log;多是 data 目录权限、端口被占、config 损坏。chroot 内用 root 跑基本排除权限问题。
  • 改了配置不生效:/etc/init.d/alist restart,别只 reload nginx。
  • 忘记 admin 密码:cd /opt/alist && ./alist admin random 或 set 新密码。
  • 启动特别慢/卡「storage loading」:某些云盘 token 失效,用 ./alist storage list 查、storage disable /挂载路径 禁掉再起。
  • 全网卡不通但 127 通:检查 alist 是否被人改过绑定地址;默认就是 0.0.0.0:5244,不用特配。

六、mihomo 代理安装配置

返回目录

环境约定:Ubuntu 22.04 / arm64 / 单二进制 / /usr/local/bin / /etc/mihomo / SysV init.d / 本地 metacubexd 面板。

6.1 确认架构 + 基础依赖

uname -m          # aarch64 → 用 arm64 包;x86_64 → 换 amd64
sudo apt update
sudo apt install -y ca-certificates curl wget gzip git unzip

架构对照:aarch64=arm64,x86_64=amd64。

6.2 安装 mihomo 二进制

以已有的 mihomo-linux-arm64-v1.19.31.gz 为例(重新下也行,替换版本号):

cd /tmp
# 如果还没下载:
# wget -O mihomo-linux-arm64-v1.19.31.gz https://github.com/MetaCubeX/mihomo/releases/download/v1.19.31/mihomo-linux-arm64-v1.19.31.gz

gunzip -f mihomo-linux-arm64-v1.19.31.gz
sudo install -o root -g root -m 0755 mihomo-linux-arm64-v1.19.31 /usr/local/bin/mihomo
hash -r
mihomo -v # 应输出 mihomo/version 信息

它是单文件 gzip 二进制,不是 tar;解出来叫 mihomo-linux-arm64-v1.19.31,install 成 mihomo 即可。

6.3 配置目录与 config.yaml

sudo mkdir -p /etc/mihomo
sudo chmod 755 /etc/mihomo

方式 A:整份订阅配置 —— 把机场给的 Clash Meta 订阅全文存成 /etc/mihomo/config.yaml,然后在文件顶部补/核对这几行(顶格、无多余空格):

mixed-port: 7890
allow-lan: true
bind-address: '*'
mode: rule
log-level: info
external-controller: 0.0.0.0:9090
secret: "ZZC20010102"
external-ui: /etc/mihomo/ui
unified-delay: true
tcp-concurrent: true

内网/本机管理更安全的做法:external-controller: 127.0.0.1:9090,再用 SSH 隧道开面板,不对外暴露 9090。

方式 B:自带节点模板(手动填) —— 最小可跑(按自己节点改 proxies):

mixed-port: 7890
allow-lan: true
bind-address: '*'
mode: rule
log-level: info
external-controller: 0.0.0.0:9090
secret: "ZZC20010102"
external-ui: /etc/mihomo/ui
unified-delay: true
tcp-concurrent: true

dns:
enable: true
enhanced-mode: fake-ip
nameserver:
- 223.5.5.5
- 119.29.29.29

proxies: []
proxy-groups:
- name: PROXY
type: select
proxies: ["auto"]
rules:
- GEOIP,CN,DIRECT
- MATCH,PROXY

方式 C:用 proxy-providers 远程订阅(不把节点写死):

mixed-port: 7890
allow-lan: true
bind-address: '*'
mode: rule
log-level: info
external-controller: 0.0.0.0:9090
secret: "ZZC20010102"
external-ui: /etc/mihomo/ui

proxy-providers:
mysub:
type: http
url: "https://你的订阅地址"
interval: 3600
health-check:
enable: true
interval: 600
url: https://www.google.com/generate_204

proxy-groups:
- name: PROXY
type: select
use: [mysub]
- name: auto
type: url-test
use: [mysub]
url: https://www.google.com/generate_204
interval: 300

rules:
- GEOIP,CN,DIRECT
- MATCH,PROXY

6.4 Geo 数据(用到 GEOIP/GEOSITE/规则库时)

mihomo 首次启动通常会自动拉 geoip/geosite;离线或想预置,把 geoip.metadb / geosite.dat / Country.mmdb 放到 /etc/mihomo。规则里只用 GEOIP,CN 且首次起不来再补;纯手动 proxies 不依赖 geo 也可先跑。

6.5 安装本地面板 UI(metacubexd)

sudo rm -rf /etc/mihomo/ui
sudo git clone -b gh-pages https://github.com/MetaCubeX/metacubexd.git /etc/mihomo/ui
sudo chmod -R 755 /etc/mihomo/ui

更新面板:

sudo git -C /etc/mihomo/ui pull -r

备选 yacd-meta:

sudo mkdir -p /etc/mihomo/ui
cd /tmp && wget -O yacd.zip https://github.com/MetaCubeX/Yacd-meta/releases/latest/download/Yacd-meta.zip
sudo unzip -o yacd.zip -d /etc/mihomo/ui

6.6 前台先验证(上 init 前必做)

sudo pkill -9 mihomo 2>/dev/null; sleep 1
sudo /usr/local/bin/mihomo -d /etc/mihomo
# 或指定文件:
# sudo /usr/local/bin/mihomo -d /etc/mihomo -f /etc/mihomo/config.yaml

正常应看到 mixed listening 7890、external controller listening 9090、ui serve 等。仅校验配置不启动:

sudo /usr/local/bin/mihomo -d /etc/mihomo -t

另开终端测:

curl -x http://127.0.0.1:7890 https://api.ipify.org
curl -s -H "Authorization: Bearer ZZC20010102" http://127.0.0.1:9090/version

都通再走下一步。Ctrl+C 停掉前台。

6.7 SysV init.d 脚本

/etc/init.d/mihomo:

#!/bin/sh
### BEGIN INIT INFO
# Provides: mihomo
# Required-Start: $network $remote_fs $syslog
# Required-Stop: $network $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Mihomo proxy daemon
# Description: Mihomo (Clash Meta) core via SysV init
### END INIT INFO

PATH=/sbin:/usr/sbin:/bin:/usr/bin
DAEMON=/usr/local/bin/mihomo
NAME=mihomo
DESC="Mihomo proxy"
HOMEDIR=/etc/mihomo
CONFIG=$HOMEDIR/config.yaml
PIDFILE=/var/run/$NAME.pid
LOGFILE=/var/log/$NAME.log
DAEMON_ARGS="-d $HOMEDIR -f $CONFIG"
USER=root

. /lib/lsb/init-functions

do_start()
{
start-stop-daemon --start --quiet --pidfile "$PIDFILE" \
--make-pidfile --background --no-close \
--chuid "$USER" --user "$USER" \
--exec "$DAEMON" -- $DAEMON_ARGS \
>> "$LOGFILE" 2>&1
}

do_stop()
{
start-stop-daemon --stop --quiet --pidfile "$PIDFILE" --retry=TERM/30/KILL/5
rm -f "$PIDFILE"
}

case "$1" in
start)
log_daemon_msg "Starting $DESC" "$NAME"
do_start
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
log_end_msg $?
;;
restart|reload|force-reload)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
sleep 1
do_start
log_end_msg $?
;;
status)
status_of_proc -p "$PIDFILE" "$DAEMON" "$NAME" && exit 0 || exit $?
;;
*)
echo "Usage: $0 {start|stop|restart|status}" >&2
exit 3
;;
esac

授权 + 注册:

sudo chmod 755 /etc/init.d/mihomo
sudo touch /var/log/mihomo.log && sudo chown root:root /var/log/mihomo.log
sudo update-rc.d mihomo defaults
sudo service mihomo start
sudo service mihomo status

Ubuntu 22.04 是 systemd,但有 LSB 头的 init.d 会被 systemd-sysv-generator 兼容,service 可用;别同时再建 /etc/systemd/system/mihomo.service,否则会抢名。

自定义启动顺序:

sudo update-rc.d -f mihomo remove
sudo update-rc.d mihomo start 99 2 3 4 5 . stop 01 0 1 6 .

6.8 防火墙 + 面板访问

sudo ufw allow 9090/tcp   # 面板;公网建议别开,用SSH隧道
sudo ufw allow 7890/tcp # 代理;只给本机/内网就别对公网开

本机/内网浏览器:http://服务器IP:9090/ui,API 填 http://服务器IP:9090,Secret 填配置里的。公网机器更稳妥:

ssh -N -L 9090:127.0.0.1:9090 用户@服务器
# 本机浏览器开 http://127.0.0.1:9090/ui

并把配置改回 external-controller: 127.0.0.1:9090。

6.9 终端走代理(按需)

临时开启:

export HTTP_PROXY=http://127.0.0.1:7890
export HTTPS_PROXY=http://127.0.0.1:7890
export ALL_PROXY=socks5://127.0.0.1:7890
# 关闭:
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY

写 ~/.bashrc 用函数:

cat >> ~/.bashrc <<'EOF'
proxy_on(){ export HTTP_PROXY=http://127.0.0.1:7890 HTTPS_PROXY=http://127.0.0.1:7890 ALL_PROXY=socks5://127.0.0.1:7890; echo proxy on; }
proxy_off(){ unset HTTP_PROXY HTTPS_PROXY ALL_PROXY; echo proxy off; }
EOF
source ~/.bashrc

查看代理环境变量是否已设置:

# 逐个查看
echo $HTTP_PROXY
echo $HTTPS_PROXY
echo $ALL_PROXY

# 或一次性过滤所有含 proxy 的变量(不区分大小写)
env | grep -i proxy

若输出显示了你设置的地址(如 http://127.0.0.1:7890),说明设置成功;若为空,则未生效。

6.10 备份/升级/排错

备份 UI 与配置:

sudo tar czf /root/mihomo-ui-$(date +%F).tar.gz -C /etc/mihomo ui
sudo cp /etc/mihomo/config.yaml /root/mihomo-config-$(date +%F).yaml

升级:

sudo service mihomo stop
sudo cp /usr/local/bin/mihomo /usr/local/bin/mihomo.bak
# 下新 arm64 .gz → gunzip → install 到 /usr/local/bin/mihomo
sudo service mihomo start

常见不通:

  • 9090 拒绝:ss -ltnp | grep 9090 看是否监听;配置里 external-controller 是否顶格、进程是否真在跑。
  • 面板 404:external-ui 路径是否有 index.html;用绝对路径 /etc/mihomo/ui。
  • 代理无网络:mixed-port 是否被占;curl -x 127.0.0.1:7890 https://api.ipify.org 看出口 IP;DNS fake-ip 与系统 resolved 冲突可先关 dns 测试。
  • YAML 报错:用 mihomo -d /etc/mihomo -t 校验,重点查缩进、冒号后空格、secret 引号。

执行顺序建议:装二进制 → 配置 → UI → 前台验证 → init.d 自启 → 防火墙 → 终端代理,基本一次通。需要 TUN 透明网关版再单独说。


七、frpc 内网穿透(ChmlFrp)

返回目录

frpc -c /etc/chmlfrp/test.ini 就是按该 ini 前台启动 ChmlFrp 客户端。先别急着加后台,按下面顺序测,能少走弯路。

7.1 先确认文件和二进制

ls -l /etc/chmlfrp/test.ini
which frpc # chroot 内应指向 /usr/local/bin/frpc 或 /opt/.../frpc
frpc --version

文件不存在会直接报 open /etc/chmlfrp/test.ini: no such file or directory;二进制不在 PATH 就用绝对路径,比如 /opt/chmlfrp/frpc -c /etc/chmlfrp/test.ini。

7.2 test.ini 最小可用模板(反代 nginx 80)

ChmlFrp 面板一般会直接生成配置,最省事是把面板「配置文件」整段贴进 test.ini;自己手写参考:

[common]
server_addr = 控制台给的服务器地址或域名
server_port = 7000
token = 控制台给的token
login_fail_exit = false

[zzc-nginx-tcp]
type = tcp
local_ip = 127.0.0.1
local_port = 80
remote_port = 控制台分配的外网端口
  • frpc 跑在 chroot、nginx 也跑在 chroot → local_ip=127.0.0.1、local_port=80
  • frpc 跑 Android 侧、nginx 在 chroot 用 192.168.5.14 互通 → local_ip=192.168.5.14
  • 有备案域名、平台给 HTTP 隧道时改用:
[zzc-nginx-http]
type = http
local_ip = 127.0.0.1
local_port = 80
custom_domains = blog.你的域名.com

老包若用 auto_token= 或 tls_enable= 就按面板生成来,别自己混改。

7.3 前台先跑看日志

frpc -c /etc/chmlfrp/test.ini

正常会看到类似 start proxy success / proxy [zzc-nginx-tcp] tcp local_port 80 ...,然后用外网 http://服务器地址:remote_port 访问。

常见报错对照:

  • dial tcp x:7000 i/o timeout → 控制面板地址/端口错,或 chroot 出网被挡;先 nc -vz 服务器地址 7000 测
  • authorization failed / token error → ini 的 token 和面板不一致,复制粘贴,别加空格
  • port already used / remote port x already registered → 换 remote_port 或在面板改端口
  • login fail 但网络通 → server_port 不对、token 错、服务端没起对应协议
  • 启动成功但浏览器打不开 → 查 nginx 本地 curl 127.0.0.1 是否还显示默认页、local_ip 是否填错、面板隧道是否「在线」

7.4 确认 nginx 本地先通

curl -s http://127.0.0.1 | head -c 200
ss -tlnp | grep ':80'

如果本地还是 nginx 默认页而不是博客,先按第二章把 default site 删了、sites-enabled 只留 zzcblog、nginx -t && service nginx reload,再测 frpc。

7.5 没问题再改后台 + 保活

手动后台:

nohup frpc -c /etc/chmlfrp/test.ini >>/var/log/frpc.log 2>&1 &
tail -f /var/log/frpc.log

接入 /opt/start_all.sh:chroot 无 systemd,用 nohup + 保活脚本即可。启动部分追加:

# ChmlFrp
if command -v frpc >/dev/null 2>&1; then
pgrep -x frpc >/dev/null || nohup frpc -c /etc/chmlfrp/test.ini >>/var/log/frpc.log 2>&1 &
fi

保活循环里再加一句同样的:

pgrep -x frpc >/dev/null || nohup frpc -c /etc/chmlfrp/test.ini >>/var/log/frpc.log 2>&1 &

改动说明:

  1. 启动部分:在 nginx、silo、alist 之后,添加 frpc 的后台启动命令,使用 nohup 和日志重定向。
  2. 保活循环:在循环末尾添加 pgrep -x frpc 检查,如果进程不存在则重新启动。
  3. 条件判断:用 command -v frpc 检查 frpc 是否存在于 PATH 中,避免因缺失二进制导致脚本报错。

如果 frpc 不在 /usr/local/bin 而是其他路径,请将 frpc 替换为绝对路径,例如 /opt/chmlfrp/frpc。整合后的完整脚本见第八章。


八、附录:开机自启全套脚本(Magisk + start_all.sh)

返回目录

以下两个脚本构成完整的开机自启链路:Magisk 脚本(宿主机 Android 侧)负责挂载目录并拉起 chroot;start_all.sh(chroot 内)负责启动全部服务并保活。直接覆盖对应文件即可。

8.1 Magisk 开机脚本 chroot_server.sh

宿主机侧,路径 /data/adb/service.d/chroot_server.sh:

cat > /data/adb/service.d/chroot_server.sh << 'EOF'
#!/system/bin/sh

sleep 30

CHROOT_DIR="/data/linux"

if mountpoint -q "$CHROOT_DIR/proc"; then
exit 0
fi

# Global wake lock - keep phone awake for all services
echo "chroot_wakelock" > /sys/power/wake_lock
echo "N" > /sys/module/wlan/parameters/power_save 2>/dev/null

mount --bind /dev "$CHROOT_DIR/dev" 2>/dev/null
mount --bind /proc "$CHROOT_DIR/proc" 2>/dev/null
mount --bind /sys "$CHROOT_DIR/sys" 2>/dev/null
mount -t devpts -o gid=5,mode=0620 devpts "$CHROOT_DIR/dev/pts" 2>/dev/null
mount -t tmpfs -o mode=1777 tmpfs "$CHROOT_DIR/tmp" 2>/dev/null
mount -t tmpfs -o mode=0755 tmpfs "$CHROOT_DIR/run" 2>/dev/null

if [ -L "$CHROOT_DIR/etc/resolv.conf" ] || [ ! -f "$CHROOT_DIR/etc/resolv.conf" ]; then
rm -f "$CHROOT_DIR/etc/resolv.conf"
echo "nameserver 8.8.8.8" > "$CHROOT_DIR/etc/resolv.conf"
echo "nameserver 114.114.114.114" >> "$CHROOT_DIR/etc/resolv.conf"
fi

chroot "$CHROOT_DIR" /opt/start_all.sh &
EOF
chmod 755 /data/adb/service.d/chroot_server.sh

说明:mountpoint -q 判断避免重复挂载;wake_lock 保证服务长期运行不掉线;chroot ... & 后台执行,不阻塞 Magisk 启动流程。

8.2 chroot 统一启动脚本 start_all.sh(含 frpc 保活)

chroot 内(先 chroot /data/linux /bin/bash),路径 /opt/start_all.sh。在 1.3 基础版 基础上加入 frpc 的启动与保活:

cat > /opt/start_all.sh << 'EOF'
#!/bin/bash
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
unset TMPDIR TMP

mkdir -p /run/sshd /tmp
chmod 755 /run/sshd
chmod 1777 /tmp

# 启动所有服务
/usr/sbin/sshd
/usr/sbin/nginx
/etc/init.d/silo start
/etc/init.d/alist start

# 启动 frpc 内网穿透(后台运行)
if command -v frpc >/dev/null 2>&1; then
pgrep -x frpc >/dev/null || nohup frpc -c /etc/chmlfrp/test.ini >>/var/log/frpc.log 2>&1 &
fi

# 如果需要启动 Node.js 应用,在这里添加:
# cd /opt/myapp && node app.js >> /var/log/myapp.log 2>&1 &

# 等待所有服务就绪
sleep 3

# 二次确认 AList 端口(最多等6秒)
for i in 1 2 3; do
if ss -tlnp | grep -q ':5244'; then
break
fi
sleep 2
done

# 保活循环(每5分钟检查一次,后台运行)
(
while true; do
sleep 300
pgrep -x sshd || /usr/sbin/sshd
pgrep -x nginx || /usr/sbin/nginx
pgrep -f '/usr/local/bin/silo' || /etc/init.d/silo start
pgrep -f '/opt/alist/alist server' || /etc/init.d/alist start
# 保活 frpc
pgrep -x frpc >/dev/null || nohup frpc -c /etc/chmlfrp/test.ini >>/var/log/frpc.log 2>&1 &
done
) &
exit 0
EOF
chmod 755 /opt/start_all.sh

8.3 端口自检

重启手机,或手动执行 sh /data/adb/service.d/chroot_server.sh 测试,然后验证所有端口:

ss -lntp | grep -E ':22|:80|:9000|:9001|:5244'