Pairing a virtual Apple Watch with a virtual iPhone
A watchOS 26.4 (Watch7,18) guest pairs with an iOS 26 guest through the phone's Watch app, and the initial sync completes. No Bluetooth hardware is involved: Apple's VM builds carry the plumbing.
Checked on 2026-10-20: a watchos watch and a minimal iPhone17,3 of iOS 26.4
(23E246), both on cloudOS 26.4 (23E244) and on the NAT network with peers
reachable, paired from the phone's Watch app. A minimal phone comes up in
Setup Assistant (it has no template); skip it (setup.skip) or go through it
before opening the Watch app.
How pairing works between VMs
- Discovery and link: NetworkRelay over IP. On a VM, nanoregistryd pairs
through NetworkRelay (
networkrelay_pairing || IsVirtualDevice), and terminusd runs onlyNRLinkManagerWired. The watch advertises Bonjour_nrd._tcp, the phone browses for it, and the PIN pairing runs over IKEv2. - Virtual Bluetooth. On a VM, bluetoothd drives a built-in virtual LE
controller (BTVC) whose radio is Bonjour
_btvc._tcp. It needs the device tree nodearm-io/bluetoothwithtransport-encoding = 10, which vphone600's tree has (and the research board conversion adds), andkern.hv_vmm_present. The feature flagBluetoothFeatures/EnableBTVCLeConnectiongates its LE connections and is off as shipped. - Version gate.
NRPairingCompatibilityVersionInfo: watchOS 26.4 is version 26; iOS 26.4 accepts 25 to 26, iOS 18.1 at most 23. The phone has to run iOS 26.
What the VMs need
The flag needs a reboot to change at runtime: feature flags are cached in
shared memory (com.apple.featureflags.shm), so restarting bluetoothd is not
enough. A runtime override goes in
/var/preferences/FeatureFlags/Domain/BluetoothFeatures.plist.
Pairing
- Create the watch (
vm create --device Watch7,18, presetwatchos) and an iOS 26 phone, and start both. - On the phone, open the Watch app (
com.apple.Bridge): Start Pairing → Set Up for Myself. The phone lists the watch as "Apple Watch NNNNN"; choose it. - The watch shows a six-digit code. Type it in the phone's window: the VM has a hardware keyboard, so there is no on-screen keypad.
- Continue on the phone. "Apple Watch is Syncing" takes about 8 minutes:
three pairedsyncd activities (
appconduitd300 s,appconduitd.appstate60 s,tccd60 s) get no answer from the watch and end by timeout. The sync then reaches 100 %.
Pitfalls
- A phone built from a template with the
trimmedservice profile (thestandarddefault) has the pairing daemons off:terminusd(NetworkRelay),identityservicesd,nanobackupd,nanoprefsyncd,nanotimekitcompaniond,security.otpaird,watchpresenced,magicswitchd.companion. Build the phone withminimal, or keep those labels (allow) when applying the profile. - If the phone remembers an unfinished pairing, choose Start Over.
- Killing only the watch's Setup leaves stale pairing managers in terminusd, and the phone can connect to one whose code no screen shows. Restart terminusd, nanoregistryd and Setup together.
- A tap on a dimmed watch display only wakes it; press the crown (Home) first.
- The watch's Setup crashed in a UIKit layout trap ("Screen class invalid") on the language list before the watch display patches; pairing from the phone avoids that screen.