Turn a phone or USB modem into a NAT gateway for your LAN.
1.6K
Turn a phone or USB modem into a NAT gateway for your local network — in a single Docker container.
Built for the case where your ISP goes down and you need to fail over to mobile data quickly, without polluting the host OS. Works with iPhone, Android, and USB 4G/5G modems. Runs on Raspberry Pi (Alpine, Raspberry Pi OS, Debian) and any Linux host with USB access.
Phone or USB modem ──USB──> Linux host running this container ──LAN──> Your devices
• usbmuxd pairs iPhones (optional)
• iptables MASQUERADE on the WAN iface
• IP forwarding enabled in host namespace
The container runs with network_mode: host so it operates directly on the host's network stack. The kernel (or usbmuxd, for iPhones) brings up the tethered device as a USB network interface; the entrypoint detects it and installs NAT rules.
| Device type | Interface name | Works out of the box? | Notes |
|---|---|---|---|
| iPhone (Personal Hotspot) | eth1, usb0 | ✅ (needs usbmuxd) | Enable Personal Hotspot, tap "Trust" on first connect |
| Android (USB tethering) | usb0, rndis0 | ✅ | Toggle "USB tethering" in settings after plugging in |
| USB 4G/5G modem (NCM/CDC) | usb0, eth1 | ✅ | Many Huawei E3372, ZTE, etc. |
| USB 4G/5G modem (MBIM/QMI) | wwan0 | ⚠️ | Kernel exposes wwan0; you must run mbimcli/qmicli first |
| USB modem in "modem mode" | /dev/ttyUSB* | ❌ | Requires pppd/wvdial; out of scope for this container |
docker run -d \
--name mobile-uplink \
--restart unless-stopped \
--network host \
--cap-add NET_ADMIN --cap-add SYS_ADMIN \
--device /dev/bus/usb:/dev/bus/usb \
-v mobile-uplink-lockdown:/var/lib/lockdown \
-e LAN_IF=eth0 \
luizmoreiradev/mobile-uplink:latest
Or with Compose (docker-compose.yml included in the repo):
docker compose up -d
docker compose logs -f
You should see Uplink ACTIVE ✓ once everything is up. Then point your LAN clients at the host as their default gateway — see DHCP setup.
lockdown volume persists pairing)Leave ENABLE_USBMUXD=true (the default).
You can set ENABLE_USBMUXD=false to skip starting usbmuxd and drop SYS_ADMIN.
Just plug it in. Most modern modems present usb0 or eth1 with DHCP from the carrier — no extra configuration needed.
Set ENABLE_USBMUXD=false.
The kernel creates wwan0, but it has no IP until you authenticate with the carrier. You need mbimcli or qmicli on the host:
# example for MBIM
mbim-network /dev/cdc-wdm0 start
Once wwan0 has an IP, this container picks it up via WAN_PATTERN.
| Env var | Default | Description |
|---|---|---|
LAN_IF | eth0 | Host interface facing your LAN |
WAN_PATTERN | ^(eth[1-9][0-9]*|usb[0-9]+|enx.*|wwan[0-9]+|rndis[0-9]+)$ | Regex matching the WAN interface |
ENABLE_USBMUXD | true | Start usbmuxd (required for iPhone, harmless otherwise) |
DETECT_TIMEOUT | 300 | Seconds to wait for the WAN interface to appear |
DHCP_TIMEOUT | 60 | Seconds to wait for the WAN interface to receive an IP |
In docker-compose.yml, remember to double the $ in the regex ($$) so Compose does not interpolate.
The container only handles the uplink and NAT. You still need a DHCP server on the LAN telling clients to use this host as their gateway.
If you already run Pi-hole, dnsmasq, or your router as DHCP server, set the Router / Default Gateway option to this host's IP. To force clients to pick up the new gateway immediately:
# macOS
sudo ipconfig set en0 BOOTP && sudo ipconfig set en0 DHCP
# Linux (NetworkManager)
nmcli connection down "<name>" && nmcli connection up "<name>"
From the host:
docker logs mobile-uplink
ip route # default route via WAN iface
iptables -t nat -L POSTROUTING -n -v # MASQUERADE rule
docker inspect --format='{{.State.Health.Status}}' mobile-uplink
From a LAN client (after DHCP renewal):
curl -4 ifconfig.me # should return the carrier-assigned IP
traceroute 1.1.1.1 # first hop is the host
iPhone interface never appears Plug the cable in after the container starts, and accept the "Trust" prompt on the iPhone. If the popup never appears, unlock the iPhone first, then reconnect. The lockdown volume persists pairing across restarts so you only do this once.
Android USB tethering option is greyed out Connect the cable first, then toggle the option. Some phones require you to confirm "File transfer" mode first.
Slow throughput USB 2.0 caps at ~280 Mbps. Pi 4 / 5 expose USB 3.0 ports (blue), but phones often negotiate USB 2.0 anyway — bandwidth is plenty for 4G, may bottleneck 5G mmWave.
Container restarts every few minutes The watchdog kills the container if the WAN interface disappears, so Docker can restart it cleanly. Check the cable and tethering toggle.
SYS_ADMIN makes you nervous
It's required by usbmuxd to operate on USB devices. If you don't need iPhone support, set ENABLE_USBMUXD=false and drop SYS_ADMIN from cap_add.
docker buildx build \
--platform linux/arm64,linux/amd64,linux/arm/v7 \
--build-arg VERSION="$(git describe --tags --always)" \
--build-arg BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--build-arg VCS_REF="$(git rev-parse --short HEAD)" \
-t luizmoreiradev/mobile-uplink:latest \
--push .
Tagged releases are built and pushed automatically by GitHub Actions (.github/workflows/docker-publish.yml).
MIT — see LICENSE.
Content type
Image
Digest
sha256:9961319e1…
Size
35.2 MB
Last updated
5 months ago
docker pull luizmoreiradev/mobile-uplink