Why does a React Native debug build on a USB-connected Android phone need adb reverse tcp:8081 tcp:8081 to reach Metro?
answer
- which host does the phone ask for?
- device uses localhost, emulator 10.0.2.2
- phone port 8081 forwarded over USB
- USB debugging, Android 5.0+
- re-run after reconnecting
basics
~20 sOn a physical Android device the debug app asks for the Metro dev server at localhost:8081 on the phone itself. adb reverse forwards the phone's port 8081 over USB to port 8081 on your computer, where Metro listens.
solid answer
~40 sA debug build loads its JavaScript from the Metro dev server on port 8081. React Native's Android code uses `10.0.2.2` on the stock emulator, which aliases the host machine, but on a physical device it falls back to `localhost`, meaning the phone itself, where nothing is listening. `adb reverse tcp:8081 tcp:8081` tells adb to forward the phone's port 8081 back over the USB cable to the computer's port 8081, so the request reaches Metro, and reloads, Fast Refresh and DevTools all work. The docs recommend it for Android 5.0+ with USB debugging on; with several devices you add `-s <serial>`. The mapping lives with the adb connection, so re-run it after reconnecting. Without a cable, you put both on the same Wi-Fi and set the computer's IP in Dev Settings.
code
bash · 9 lines# List attached devices and their serials
adb devices
# Forward the phone's port 8081 to the computer's port 8081 (Metro)
adb -s <device serial> reverse tcp:8081 tcp:8081
# Start Metro and run the debug build
npx react-native start
npm run androidgo deeper
Recall the command, what it forwards, and the prerequisites: USB debugging, a cable, and the device showing as device in adb devices.
Explain why the device uses localhost while the emulator uses 10.0.2.2, and when to prefer USB forwarding over the Wi-Fi host setting.
Diagnose connection failures quickly: lost mappings after reconnects, several devices without -s, non-default ports, and networks that block Wi-Fi setups.
Standardise the device-testing setup for the team so connection problems don't eat into testing time on real hardware.
## What the app is trying to reach A React Native **debug build** does not carry your latest JavaScript; it asks the **Metro dev server** on your computer for the bundle, by default on port **8081**. The question is which address the app uses for "the computer". React Native's Android code picks the dev server host like this: | Where the app runs | Host it uses | Why | |---|---|---| | Stock Android emulator | `10.0.2.2` | The emulator's alias for the host machine's loopback | | Genymotion | `10.0.3.2` | Genymotion's equivalent alias | | Physical device | `localhost` | Nothing better is known, so it assumes the port is forwarded | | Any, if set | The `metro.host` system property | An explicit override | So a phone plugged in over USB asks for `localhost:8081` **on the phone itself**, where nothing is listening. The result is a red screen saying the app could not connect to the dev server. ## What adb reverse does `adb reverse tcp:8081 tcp:8081` tells the Android Debug Bridge to **forward the phone's port 8081 back over the USB connection** to port 8081 on your computer. After that, the app's request to `localhost:8081` reaches Metro. The React Native docs call this the recommended method when: - the device runs Android 5.0 or newer; - **USB debugging** is enabled in Developer options; - the phone is connected to the computer by USB. With several devices attached, target one with `-s`, using the serial from `adb devices`: ```bash adb devices adb -s <device serial> reverse tcp:8081 tcp:8081 ``` The `device` column in `adb devices` must say `device`; `unauthorized` means the phone has not yet accepted the computer's debugging prompt. ## Why it is the recommended route - **No network dependency.** The traffic travels over the USB cable, so office Wi-Fi rules, VPNs and guest networks do not matter. - **Stable address.** `localhost` never changes, whereas your laptop's Wi-Fi IP address does. - **Everything on port 8081 works.** Reloads, Fast Refresh and React Native DevTools all talk to the same dev server port, so one mapping covers them. The mapping belongs to that adb connection, so it can disappear when the cable is unplugged or the adb server restarts. If a device that worked earlier starts showing the connection error after a reconnect, run the command again. If Metro runs on another port, forward that port instead, for example `adb reverse tcp:8082 tcp:8082`, and make sure the app is configured for the same port. ## The alternative: same Wi-Fi network Without a cable, the app can reach Metro over Wi-Fi: 1. Install the app once over USB. 2. Put the phone and the computer on the **same** Wi-Fi network. 3. Open the app; it shows a red connection error at first. 4. Open the Dev Menu, go to **Dev Settings → Debug server host & port for device**, and enter the computer's IP address and port, for example `10.0.1.1:8081`. 5. Choose **Reload JS** from the Dev Menu. This is more fragile: the laptop's IP changes between networks, and many guest or captive-portal networks block device-to-device traffic entirely. ## A quick diagnosis order When a phone shows the connection error, check in this order, cheapest first: 1. Is Metro running on the computer, on the port you expect? 2. Does `adb devices` list the phone as `device`, not `unauthorized` or `offline`? 3. Has `adb reverse` been run for this device since it was last plugged in? 4. Is the app a debug build? A release build never contacts Metro, so a connection error there means something else is wrong. Most "the phone can't see Metro" reports end at step 3. ## Mistakes interviewers listen for - Saying the emulator and the device use the same host; the emulator uses `10.0.2.2`. - Reversing the direction: this is not `adb forward`, which exposes a phone port to the computer. - Assuming the mapping is permanent. - Forgetting that a release build does not need Metro at all, because its JavaScript is bundled into the APK.
- Why doesn't the Android emulator need adb reverse?React Native detects the stock emulator and uses 10.0.2.2, the emulator's alias for the host machine's loopback, so the request reaches Metro on the computer directly. Genymotion gets 10.0.3.2. Only a physical device falls back to localhost, which is why it needs the reverse mapping or a Wi-Fi host setting.
- Metro runs on port 8082 because 8081 is taken. What changes?Forward the port Metro actually uses, adb reverse tcp:8082 tcp:8082, and make sure the app is configured for that same port, since the default is 8081. A mapping for 8081 would forward the phone's 8081 to a port where nothing listens.
- Does a release build on the same phone need adb reverse?No. A release build bundles its JavaScript into the APK and has no Dev Menu or dev server connection, so Metro can be stopped entirely. adb reverse only matters for debug builds that load code from Metro.
adb reverse is call forwarding on the phone: the app dials the phone's own extension 8081, and the forwarding rule, carried over the USB cable, rings the laptop's extension 8081 instead. Unplug the cable and the forwarding rule can be lost.
saying these in an interview costs you the question
- The phone's localhost is the developer's computer.
- The emulator and a USB device use the same dev server address.
- adb forward and adb reverse do the same thing.
- Once set, the reverse mapping survives unplugging forever.
- Release builds also need adb reverse to load JavaScript.