Fixing iPhone Wireless Backup & Automating Photo Extraction
The Setup
Z440 workstation running Linux Mint with a systemd user service (iphone-backup.service) that does daily wireless iPhone backups. The stack:
- netmuxd (jkcoxson/netmuxd) — Rust-based network muxer that discovers the iPhone via mDNS
- libimobiledevice (
idevicebackup2,ideviceinfo,idevicepair) — the actual backup tools - avahi-daemon — mDNS/DNS-SD for device discovery
- Custom Python script (
~/Documents/scripts/services/iphone_backup.py) — orchestrates everything: starts netmuxd, probes for the device, runs backup, monitors connectivity
The service runs at boot, checks every 5 minutes for the phone, and does one full incremental backup per day.
The Problem
Last successful backup: 2025-10-25 — 4 months ago. The service was running but every attempt failed with “Probe failed. Device is not ready.”
Debugging
Step 1: Check if mDNS discovery works
avahi-browse -r -t _apple-mobdev2._tcp
This showed the iPhone on the network. So the phone was visible — netmuxd just wasn’t picking it up.
Step 2: Run netmuxd with debug logging
RUST_LOG=debug netmuxd --disable-unix --host 127.0.0.1
First finding: netmuxd was looking up WiFiMACAddress in the pairing plist at /var/lib/lockdown/<UDID>.plist to match the mDNS-discovered device. There was another iPhone on the network (Erdal’s) with a different MAC that was confusing the investigation.
Step 3: Re-pair via USB
After correctly identifying my phone (Visars-iPhone at 5c:87:30:b4:22:d1), debug logs showed:
Error: "InvalidHostID"
Request: "StartSession"
The pairing certificates had expired after 4 months of no contact. iOS invalidated the trust relationship. Re-pairing via USB fixed it:
idevicepair pair
# Tap "Trust" on the iPhone
Step 4: Verify wireless connection
USBMUXD_SOCKET_ADDRESS=127.0.0.1:27015 ideviceinfo -n
Returned device info successfully. Restarted the service and backup began immediately at ~23 MB/s over 5GHz WiFi.
Automating Photo Extraction
The Goal
After each successful backup, automatically create a browsable folder of camera photos sorted chronologically (like the Photos app) — without needing to manually extract the backup.
How iPhone Backups Store Files
idevicebackup2 stores files as SHA-1 hashes in <UDID>/<first2chars>/<hash>. To find anything, you query Manifest.db:
SELECT fileID, relativePath FROM Files
WHERE domain = 'CameraRollDomain'
AND relativePath LIKE 'Media/PhotoData/CPLAssets/group%'
AND relativePath LIKE '%.HEIC';
This gives you mappings like:
df9b64732af57ad7c20c84fb991166a2d279d311 -> Media/PhotoData/CPLAssets/group134/0E4A44FA.HEIC
The actual file lives at <UDID>/df/df9b64732af57ad7c20c84fb991166a2d279d311.
Getting Photo Dates
Photos.sqlite is also in the raw backup (find its hash via Manifest.db). It has creation dates:
SELECT ZFILENAME, datetime(ZDATECREATED + 978307200, 'unixepoch')
FROM ZASSET
WHERE ZFILENAME IS NOT NULL AND ZFILENAME NOT LIKE 'IMG_%'
ORDER BY ZDATECREATED;
The 978307200 offset converts Apple’s Core Data timestamp (seconds since 2001-01-01) to Unix epoch.
IMG_% files are screenshots — filtered out to keep only actual camera photos.
The Solution
Added extract_camera_photos() to iphone_backup.py that:
- Opens
Manifest.dbfrom the raw backup - Finds
Photos.sqlitehash and opens it - Builds a filename-to-hash map for all CPLAssets photos
- Queries dates from Photos.sqlite
- Creates symlinks in
~/iPhone-backup/camera-photos/namedYYYY-MM-DD_HH-MM-SS_<original>.HEIC
Sorting by filename in any file manager now matches the Photos app order. Runs after every successful backup, wrapped in try/except so it never breaks the backup loop.
Key Detail: CPLAssets vs DCIM
- DCIM/100APPLE/ — screenshots and locally-shot photos with
IMG_names - PhotoData/CPLAssets/group/* — iCloud Photo Library synced camera photos with UUID names
The real camera photos are in CPLAssets, scattered across numbered group directories. The extraction flattens them into one folder.
Files Changed
~/Documents/scripts/services/iphone_backup.py— addedsqlite3import,extract_camera_photos()function, post-backup hook~/iPhone-backup/camera-photos/— auto-generated symlink folder/var/lib/lockdown/<UDID>.plist— refreshed by USB re-pairing
Cleanup
~/iPhone-backup/extracted_backup/ (848MB) is a stale one-time extraction from Oct 2025. No longer needed — safe to delete.