Writing ·
Backing up my iPhone to my NAS over Wi-Fi
A containerized service on my home NAS backs up my iPhone over Wi-Fi every Sunday night, with no cable and no laptop. It tries the normal Bonjour route first and falls back to a direct connection if the phone isn't found.
What it replaced
Backing up an iPhone normally means plugging it into a computer over USB and remembering to do it. I wanted it to happen on its own, to the NAS, and to land on a second disk for free: the NAS already copies itself to an external drive every night, so each phone backup is stored twice.
How it works
- Path 1, mDNS. A Debian container (Docker Compose, on a UGREEN NAS) runs
netmuxd, which finds the iPhone through mDNS (Bonjour) and connects with an existing trusted pairing record, so toidevicebackup2it looks like a USB connection. - Path 2, direct connection. If the phone isn't discovered, the backup falls back to a plain TCP connection to the phone's lockdown service on port 62078, using the
pymobiledevice3Python library. It tries a configured hint IP first, then scans the home subnet for open port 62078 and checks each candidate'sUniqueDeviceIDagainst the stored pairing record. It never auto-pairs, so other iPhones on the network never see a "Trust This Computer" prompt. That means no fixed IP is needed. - A Bash scheduler runs at 9 p.m. every Sunday (the schedule is one setting, so it can be daily). Backups are incremental, so each run only transfers what changed, into the same backup folder.
- Retries and a lock. If the phone can't be reached, it tries again every 5 minutes for up to 2 hours. A lock file stops two backups from running at once.
- Self-healing. A watchdog restarts
netmuxdif it crashes, and Docker's restart policy brings the whole service back after a NAS reboot. - A log line per run (found, started, OK or failed), kept separately for each path, so a health check is a quick look at one file.
Problems, and how I found each
1. The phone was unreachable from my laptop. ip route get showed why: my Tailscale subnet routes were sending local traffic into the VPN tunnel instead of over Wi-Fi. Rather than fight the routes, I moved the service to the NAS, which is wired and sits outside that problem.
2. The NAS couldn't discover the phone. avahi-browse traced it to Wi-Fi 7 MLO (multi-link operation): the multicast announcements on the MLO network weren't reaching wired devices. Direct port tests showed unicast still worked on MLO, so I moved the phone to the standard network for a while and then built the direct-connection path above. It removes the dependency on mDNS, so the phone can now stay on the MLO network full-time. Discovery on MLO was later confirmed to work too.
3. The container crash-looped with exit code 126. The image copied its scripts in readable only by root, while the container runs as a non-root user so the backup files stay owned by me. Setting explicit 755 permissions fixed it.
4. Remote access changed after a Tailscale login. tailscale login had reset --accept-routes to the Linux default (off), which changed how my laptop reached home-network addresses. I restored it with tailscale set and switched to Tailscale's stable 100.x addresses for remote access.
5. An async library. The current pymobiledevice3 API is async, so I read its function signatures to adapt the code, and used full=False for incremental backups. The container image moved to python:3.12-slim to include it.
Can it back up over cellular?
I tested backing up over Tailscale while the phone was on cellular. tailscale ping reached the phone, but iOS actively refused the connection on the lockdown port. My conclusion: iOS only accepts sync and backup connections over its Wi-Fi interface, not over a VPN or cellular. I wrote down two possible workarounds: a travel router running Tailscale, so the phone sees a local Wi-Fi connection, or a sideloaded on-device app that uses the idevice Rust crate's mobilebackup2 module over a loopback VPN. The on-device app is a possible future project. I haven't built either.
Keeping it locked down
- No ports are exposed to the internet.
- The pairing record is stored with restricted permissions (600 on files, 700 on folders).
- The container runs as a non-root user.
- iOS requires the passcode on the phone for each backup, as a security feature. I kept that in place instead of weakening device security, and chose Sunday at 9 p.m. so the prompt arrives when I'm usually around.
Result
Hands-off iPhone backups to the NAS every Sunday night, with a second copy on an external drive. The manual step is gone, and the setup recovers from crashes and reboots by itself. It's the same home-lab approach as the AI agent on my NAS: a small container with the least access it needs.
I built this with AI assistance, and I can walk through the troubleshooting myself.