Photo: Zhuoer Mountain, Qilian County
1. Background
My bypass router is a Xiaomi AX3000T flashed with OpenWrt, running OpenClash (built on the mihomo core). I’d been on this setup for over half a year and it was mostly fine, with one fatal flaw: the AX3000T only has 233MB of RAM.
With OpenClash running, the mihomo process alone eats tens of MB, and after the OpenWrt system’s own overhead there’s usually only a dozen-odd MB left. Add a few more subscription nodes or some heavier rules and the OOM Killer fires, mihomo gets killed, and the whole proxy drops. The annoying part is the network never comes back by itself — I had to log into the admin panel and restart the OpenClash service by hand. Sometimes it died overnight and I didn’t notice until the next morning, when nothing could reach the internet. So I decided to move mihomo off the router.
2. Choosing an approach
I happened to have a spare Raspberry Pi 4B (the 8GB version) sitting around, so memory was a non-issue. The plan: run mihomo in Docker on the Pi, and let the router stop doing proxy duty.
3. Backing up the AX3000T config
First step of the migration: pull all the router’s config off it first:
# 备份OpenWrt基础配置
scp -r root@你的路由器IP:/etc/config/ ./openwrt-config/
# 备份OpenClash的mihomo主配置
scp root@你的路由器IP:/etc/openclash/config/config_real.yaml ./mihomo-config/
# 备份规则集文件
scp -r root@你的路由器IP:/etc/openclash/rule_provider/ ./mihomo-config/rule_provider/
# 备份自定义规则
scp root@你的路由器IP:/etc/openclash/custom_rules/ ./mihomo-config/custom_rules/
4. Migrating the config
The config_real.yaml that OpenClash exports can’t be used as-is, it needs a few tweaks:
1. Delete the experimental field that only OpenClash uses (OpenClash adds it itself, mihomo doesn’t recognize it)
2. Change mixed-port, port and the other ports around so they don’t clash with other services on the Pi
3. Set allow-lan: true (other devices need to reach it over the LAN)
4. Change external-controller to 0.0.0.0:9090 for remote management
Drop the cleaned-up config and the rule_provider files into a directory on the Pi, say /home/pi/mihomo/
5. Pitfalls: the ImmortalWrt Docker route
My first idea was to run ImmortalWrt (a fork of OpenWrt) in Docker on the Pi, then run OpenClash inside ImmortalWrt. That would make the config migration the easy part, and it’d feel exactly like the router.
Sounds wonderful. In practice it’s a disaster.
Once the ImmortalWrt container starts, its br-lan bridge grabs the Pi’s eth0 NIC outright! Even worse, ImmortalWrt’s default IP is 192.168.x.1 (your router IP), while my Pi’s IP is your Pi IP, and the two addresses collide on the same subnet.
Result: the Pi went dark. The whole LAN routing table went sideways — not only was the Pi unreachable, other devices lost their network too. In the end I had to physically power off and restart the Pi, boot into recovery mode and delete that Docker container.
Blood and tears lesson: never run ImmortalWrt in Docker on the Pi as your proxy! The br-lan bridge seizes the NIC, and the IP conflict takes down the entire network!
6. The right fix: just run mihomo in Docker
After ditching the ImmortalWrt route, I went back to the simplest option — run mihomo directly with Docker:
docker run -d --name mihomo --network host --cap-add NET_ADMIN -v /home/pi/mihomo/config.yaml:/root/.config/mihomo/config.yaml -v /home/pi/mihomo/rule_provider:/root/.config/mihomo/rule_provider --restart unless-stopped metacubex/mihomo:latest
That one command starts mihomo, running on the Pi’s port 9090. Any other device that needs the proxy just points its main router and dns at the Pi’s IP.
7. How it compares
| Comparison | AX3000T + OpenClash | Raspberry Pi 4B + Docker mihomo |
|---|---|---|
| Available memory | ~233MB (usually not enough) | 8GB (totally overkill) |
| OOM risk | Fires all the time, drops the network | Basically non-existent |
| Config management | Have to log into the OpenWrt admin panel | Just edit the yaml + docker restart |
| Firmware update risk | Flashing can brick it | Totally independent of the router |
| Network outage risk | OOM kills the process, network dies | Almost never |
| Extra hardware cost | None | Need a Raspberry Pi |
| Extra power draw | None | About 5W (Raspberry Pi 4B idle) |
| Maintenance effort | Medium | Low (Docker container management is very convenient) |
8. Wrap-up
How do you tell it’s OOM? SSH into the router and run dmesg | grep -i oom. If you see a flood of “Out of memory: Killed process” logs, that’s it. The AX3000T only has 233MB, the clash_meta process running OpenClash regularly eats 100MB+, and with the system on top there’s basically not enough memory to go around.
The whole lesson in one line: don’t run your proxy in an ImmortalWrt Docker container, just use mihomo’s own Docker image. ImmortalWrt’s br-lan bridge grabs the host’s NIC, causes an IP conflict, and crashes the entire network.
The Raspberry Pi 4B + Docker + mihomo combo solves the root cause — the router not having enough memory — and it’s far easier to manage than OpenClash. The config is a single yaml file: edit it, docker restart, done. No more clicking around OpenWrt’s cluttered web interface.
If your router keeps crashing your proxy because it’s low on memory, I’d seriously give this setup a try!
References:
Docker’s official install script
Published on August 23, 2026.
