写在前面

这次升级的设备是红米 AX6000,运行的是 ImmortalWrt,原始版本为 24.10.6,目标版本为 25.12.0

这次更值得记录的是升级的协作方式。我没有凭经验直接刷机,而是让 Codex 全程参与。它先盘点设备事实、目标版本、包体系变化、备份范围、升级步骤和恢复验证,再由我审核方案;确认没有问题后,Codex 继续执行能执行的步骤,我根据输出结果决定下一轮指令。

结果很好。升级完成后,LAN、Wi-Fi、LuCI、OpenClash、WireGuard、DDNS 都恢复正常。过程中真正需要我手工接管的窗口很短:升级后先恢复 OpenClash 代理网络,随后 Codex 重新接手 WireGuard、DDNS、服务验证和文档整理。

这篇文章就是这次升级的复盘。

升级完成后的 LuCI 登录页
升级完成后的 LuCI 登录页

为什么我要把路由器升级交给 Agent?

路由器升级和普通软件升级不一样。失败以后,很难靠“重跑一次命令”解决:你可能会失去 SSH、失去 DNS、失去外网,甚至因为正在升级的路由器本身就是控制链路的一部分,导致远程操作中断。

我希望 Codex 处理的核心工作,是把升级拆成几个必须回答清楚的问题:

  1. 当前设备到底是什么 profile?
  2. 当前版本、内核、包管理器、源、已安装软件包是什么状态?
  3. 目标固件是否真的支持这个设备?
  4. 25.12.0 的包体系是否还能安装我依赖的软件?
  5. 哪些配置应该进入 sysupgrade 备份,哪些 runtime 数据应该单独备份?
  6. 升级前要怎样临时脱离 OpenClash DNS?
  7. 升级后恢复包、服务、DNS、WireGuard、DDNS 的顺序是什么?
  8. 每一步失败时应该停在哪里,怎样避免继续扩大风险?

这类问题很适合 Agent:它可以不厌其烦地把当前系统事实、官方 release 目录、目标镜像、软件包列表、备份内容和验证命令串起来,最后形成一个可执行的 checklist。

理想流程是:Codex 给方案,我审核;审核通过后,Codex 执行命令、读取输出、继续给出下一步建议;我主要负责判断方案是否合理、验证结果是否符合预期,以及在关键节点给出继续或停止的指令。

这次没有做到完全由 Codex 执行,原因在我的网络环境。Codex 访问外网依赖 OpenClash 代理,而 OpenClash 又运行在被升级的路由器上。升级后代理恢复之前,我需要手工完成最小闭环:更新 apk 索引、安装 OpenClash、恢复代理网络。之后再把后续排查、验证和文档整理交回给 Agent。


当前环境与目标版本

升级前设备状态:

设备: Xiaomi Redmi Router AX6000
board: xiaomi,redmi-router-ax6000-ubootmod
target: mediatek/filogic
arch: aarch64_cortex-a53
当前版本: ImmortalWrt 24.10.6
当前内核: 6.6.133
包管理器: opkg
root shell: /bin/bash

目标版本:

目标版本: ImmortalWrt 25.12.0
目标内核: 6.12.87
包管理器: apk
目标镜像: xiaomi_redmi-router-ax6000-ubootmod-squashfs-sysupgrade.itb

这里最关键的点是 ubootmod。红米 AX6000 有不同布局,镜像不能只看“AX6000”这个商品名,必须匹配到 xiaomi,redmi-router-ax6000-ubootmod 对应的 sysupgrade 镜像。

另一个关键点是 25.12.0 不是普通小版本升级。它跨过了包体系边界:从 opkg 迁移到 apk。这意味着升级后的恢复逻辑不能再沿用 /etc/opkg/distfeeds.confcustomfeeds.conf.ipk 软件包的习惯。

升级后的 ImmortalWrt 系统信息
升级后的 ImmortalWrt 系统信息

25.12.0 相比 24.10.6 值得升级在哪里?

对我来说,25.12.0 的吸引力主要来自三个方面:新内核、新包体系,以及个人维护习惯。

第一是内核版本。我的 24.10.6 运行在 kernel 6.6.133,25.12.0 对应 kernel 6.12.87。路由器这种设备不需要追新内核,但内核分支更新通常意味着驱动、网络栈、安全修复和 kmod 生态都会进入新的维护线。只要目标设备 profile 和关键包兼容,这本身就是一个合理的升级理由。

第二是包管理器变化。OpenWrt 25.12 系列将默认包管理器从 opkg 切换到 apkopkg 是 OpenWrt 多年来的传统方案,但长期维护和依赖处理能力已经显得吃力;apk 来自 Alpine 生态,仍在活跃维护,索引、依赖解析和命令体验都更现代。

从日常维护角度看,最直接的变化是:

  • 软件源从 distfeeds.conf 迁移到 /etc/apk/repositories
  • 安装、查询、升级、删除命令全部换成 apk 语法
  • 排查软件包问题时,要围绕 apk 仓库索引和 apk 命令来查,而不是继续看 opkg 状态

第三是我的个人偏好:基础设施可以稳定,但不能长期落后太多。过去我会因为步骤繁琐、失败成本高而推迟升级;这次有 Agent 加持,盘点、比对、备份清单、恢复顺序和验证命令都可以交给 Codex 处理,升级负担明显降低。

24.10.6 作为 24.10 系列的 service release,仍然适合作为稳定维护版本;25.12.0 则是新稳定分支的起点。对路由器这种基础设施来说,新分支不等于自动更好。这次我的判断是:设备 profile 明确支持、关键包能从官方源恢复、DNS 和 OpenClash 风险可控,升级收益大于维护成本。


升级前必须做的检查

这部分是整个方案里最重要的部分。固件升级的风险通常在执行 sysupgrade 之前就已经埋下了:设备 profile、备份范围、DNS 链路、包源和恢复顺序,只要有一项判断错,后面都会变得被动。

1. 确认设备 profile

至少要确认:

ubus call system board
cat /etc/openwrt_release

重点看 board_nametargetarch 和当前版本。镜像必须匹配 ubootmod,不能拿普通布局镜像碰运气。

2. 确认备份空间和运行状态

df -h / /overlay /tmp
ip route
wifi status

/tmp 要能放下固件,/overlay 要有足够空间处理备份和临时配置。路由器升级时不要通过 WireGuard 远程执行,因为升级会中断网络栈,最好在本地网络内操作。

3. 备份关键配置

我把备份分为两类。

第一类是 sysupgrade 应该保留的配置,例如:

/etc/config/network
/etc/config/wireless
/etc/config/firewall
/etc/config/dhcp
/etc/config/ddns
/etc/config/openclash
/etc/config/system
/etc/config/luci
/etc/dropbear/authorized_keys
/etc/passwd
/etc/group
/etc/shadow
/etc/crontabs/root

第二类是只做离线备份、不放进 sysupgrade 的 runtime 数据,例如 OpenClash 的 core、规则库、Geo 数据和历史缓存。这些东西体积大、状态复杂,不应该全部塞进系统升级保留列表。

4. 收窄 sysupgrade 保留范围

这是 Agent 方案里我认为最有价值的一步:不要把旧系统里所有看起来“有用”的目录都保留下来。

尤其要避免:

/etc/opkg
/usr/share/openclash
/etc/openclash 全目录
/data
/www

25.12.0 已经切到 apk,保留 /etc/opkg 只会把旧包体系的历史状态带进新系统。OpenClash runtime 也应该单独备份、升级后再恢复。

5. 临时脱离 OpenClash DNS

我原来的 DNS 链路依赖 OpenClash:

dnsmasq -> 127.0.0.1#7874 -> OpenClash

升级后 OpenClash 尚未恢复时,如果 DNS 仍指向 127.0.0.1#7874,就会出现“Wi-Fi 连接正常、WAN 也正常、但不能解析域名”的假死状态。

所以升级前先把 DNS 临时改回普通上游,确认:

nslookup openwrt.org 127.0.0.1

能正常解析以后再继续。

6. 临时把 root shell 切回 ash

我的 root shell 原来是 /bin/bash。升级后如果 bash 尚未安装,而 /etc/passwd 仍指向 /bin/bash,SSH 登录可能会出问题。

所以升级前先临时切回 /bin/ash,升级后确认 bash 安装成功,再切回去。

7. 固件校验和兼容性测试

固件下载后先做 sha256 校验,再复制到路由器,用:

sysupgrade -T <firmware.itb>

只有没有 device mismatch、format error、compat error 时才正式升级。


需要规避的坑

不要混用 opkg、ipk 和 apk

这是 25.12.0 升级最大的边界。

升级后不要把 24.10 的第三方 .ipk 源写入 /etc/apk/repositories,也不要试图用 opkg 恢复旧包。一个第三方源要能在 25.12 上使用,至少要满足:

1. 包文件是 .apk
2. 仓库有 packages.adb
3. arch 是 aarch64_cortex-a53
4. 版本线是 25.12
5. 不混入 24.10/opkg/ipk 包

不要把 OpenClash 当成普通配置目录备份

/etc/config/openclash 可以保留,但 /etc/openclash 里有很多 runtime 数据。我的做法是:配置走 sysupgrade,runtime 单独打包,升级后在 OpenClash 停止状态下恢复,再启动服务。

不要过早恢复 OpenClash DNS

只有 OpenClash 已经启动,并且监听 7874 后,才把 dnsmasq 的上游切回 127.0.0.1#7874

不要远程跨 WireGuard 升级

WireGuard 会跟着 network 和内核模块一起重启。正式升级时不要依赖它作为控制通道。

不要批量恢复历史软件包

opkg list-installed 可以作为参考,但不能升级后无脑批量安装。正确做法是只恢复已确认存在于 25.12 官方 apk 源的关键包,再逐项验证服务。

root shell 的 bash/ash 切换要闭环

如果 root shell 指向 /bin/bash,升级前最好先临时切回 /bin/ash。新系统起来后,bash 可能还没安装;这时 /etc/passwd 继续指向 /bin/bash,SSH 登录就可能把自己锁在门外。

恢复阶段也要反过来检查:确认 bash 已安装、/bin/bash 存在,再把 root shell 切回去。

Agent 依赖 OpenClash 时要预留人工接管窗口

这次的限制很具体:OpenClash 没恢复前,Codex 可能无法稳定访问外网。方案里要提前标出这个人工接管窗口:升级后先由人手工 apk update、安装 OpenClash、恢复代理;OpenClash 正常后,再把操作权交回给 Agent。


我这次的两个特别操作

1. 从 USTC 源改回官方源

我之前因为网络速度问题,把源换成了 USTC 镜像。升级前的直觉是:继续用镜像会更快。

但实际恢复时发现,USTC 镜像缺少某些关键包,尤其是某科学上网应用相关包。反而官方 downloads.immortalwrt.org 源里的包更完整,并且在我的网络环境下不需要科学上网就能访问。

所以最终方案是:25.12.0 恢复阶段只使用官方源,不使用 USTC,不启用第三方源。先保证系统和关键服务恢复,再考虑镜像加速。

我的 /etc/apk/repositories 使用官方 25.12 源,包括 target packages、kmods 和 aarch64_cortex-a53 下的 base、luci、packages、routing、telephony。

2. 唯一的人工接管窗口:恢复 OpenClash

升级后我的手工操作集中在恢复代理网络:

apk update
apk add luci-app-openclash luci-compat

OpenClash 恢复并且代理可用后,我把后续工作交回给 Codex。WireGuard 的包安装、network restart、接口验证和日志检查都是 Codex 接手后处理的内容。


升级后的恢复顺序

最终恢复顺序分成两段:

  1. 人工恢复代理:基础验证,确认 apk/etc/apk/repositories,使用官方源 apk update,安装 OpenClash 并恢复代理网络。
  2. Codex 接手收尾:安装 bash、WireGuard、DDNS、Argon、bridger 等关键包;把 root shell 切回 /bin/bash;恢复 OpenClash runtime 和 DNS;重启 network;验证 WireGuard、DDNS、LuCI、客户端访问和日志。

这个顺序的好处是每一步都有明确的前置条件。DNS 不会早于 OpenClash 恢复,bash 不会早于软件包安装;WireGuard 也不只验证包列表,还要验证 network handler 和实际接口。


apk 常用法速记

25.12 之后,日常维护需要把 opkg 习惯切换到 apk

更新索引:

apk update

安装软件包:

apk add bash luci-app-openclash wireguard-tools

搜索软件包:

apk search openclash
apk search wireguard

查看已安装包:

apk list --installed
apk list --installed | grep wireguard

查看包信息:

apk info bash
apk info -a bash

删除软件包:

apk del <package>

查看和编辑源:

cat /etc/apk/repositories
vi /etc/apk/repositories
apk update

opkg 相比,最需要重新建立肌肉记忆的是:不要再去改 /etc/opkg/distfeeds.conf,不要把 .ipk 当成可安装包,也不要混用旧源。


这次使用 Agent 的体会

这次升级让我更确认一件事:Agent 能显著降低基础设施维护的操作负担。除了写方案,它还可以执行大量繁琐、重复、容易漏项的步骤,把升级任务拆成可验证的阶段。

它真正帮上忙的地方包括:

  • 从设备事实出发,不把“AX6000”当成唯一判断依据
  • 把 25.12.0 的 apk 迁移识别为主要风险边界
  • 把备份拆成 sysupgrade 配置和 runtime 数据两类
  • 提前处理 OpenClash DNS 和 bash shell 这种容易把自己锁在门外的问题
  • 明确哪些第三方源不能迁移,哪些包可以从官方源恢复
  • 给每个阶段安排验证命令
  • 执行繁琐命令、读取结果,再根据结果提出下一轮操作建议

人的角色也变得更清晰:我不需要把精力消耗在复制命令、整理包列表、反复查路径这类机械操作上,可以把注意力放在审查方案、观察关键输出、判断恢复结果和给 Agent 下达下一轮指令。任务推进速度比纯手工方式快很多,思路也更稳定。

我的结论是:路由器升级可以让 Agent 深度参与,但不适合做成无人值守脚本。最稳的方式是让 Agent 负责计划、执行、校验和信息整理,人负责审查、授权、接管网络中断阶段,并在关键节点做判断。


总结

红米 AX6000 从 ImmortalWrt 24.10.6 升级到 25.12.0 是可行的,但这次升级要按 opkg -> apk 迁移来准备,不能按普通小版本升级处理。

这次升级最重要的经验有四条:

  1. 先确认 ubootmod profile、固件 sha256 和 sysupgrade -T
  2. 升级前收窄备份范围,不把旧 opkg 和大块 runtime 状态带进新系统
  3. 升级后优先使用官方 apk 源,先恢复关键服务,再考虑第三方源
  4. OpenClash 恢复前需要人工接管;恢复后再交给 Codex 处理 WireGuard、DDNS 和后续验证

如果你也要升级类似设备,我建议先让 Agent 做一份基于你当前机器事实的方案,再逐项执行。不要直接套别人的命令,尤其不要忽略 board profile、包源、DNS 和第三方插件这几个边界。