adb was built for a USB cable and, later, a local network. It works over the internet too, and plenty of teams depend on that: a CI runner in one region talking to phones in another, a developer at home reaching the office rack, a test suite driving cloud phones. The defaults, though, assume a trusted network. Get the setup wrong and a debug bridge becomes a remote shell for anyone who finds it.
This guide explains how adb talks, four safe ways to stretch it over the internet with exact commands, and a checklist for keeping it closed.
How adb talks
Three pieces are involved. The adb client is the command you type. The adb server is a background process on the same machine, listening on 127.0.0.1:5037, that keeps track of devices. adbd is the daemon on the phone. The server reaches adbd over USB, over plain TCP after adb tcpip, or over TLS with wireless debugging on Android 11 and later.
Going remote means stretching one of those hops across the internet: client to server, or server to adbd. Which hop you stretch decides the method.
Two facts shape the security picture. adbd asks the user to authorize each computer's RSA key once, which keeps out strangers the phone has never seen. But classic TCP mode encrypts nothing: shell output, logcat and every file you push travel in clear text.
What never to do
Do not forward port 5555 or 5037 to the internet on your router, and do not open them in a cloud firewall. In 2018 a worm known as ADB.Miner spread through Android devices that exposed adb on port 5555, mostly TV boxes, and turned them into cryptocurrency miners. Scanners look for open adb ports constantly.
Also avoid starting the adb server with -a, which makes it listen on all interfaces. Anyone who reaches that port drives every device attached to the host, no key dialog involved.
Way 1: SSH tunnel to a remote adb server
Use this when the phones are on USB at a host you can SSH into: an office rack, a lab machine, a CI box. Forward a local port to the host's adb server and point your client at it:
ssh -N -L 15037:127.0.0.1:5037 user@lab-host adb -P 15037 devices ADB_SERVER_SOCKET=tcp:127.0.0.1:15037 adb -s SERIAL logcat -d
Use a local port other than 5037, so your own adb server does not collide with the tunnel. Installs, pushes, shell and logcat all work, because the client streams everything through its connection to the server.
One surprise: adb forward now opens its port on the lab host, not on your laptop. To reach a forwarded port from your machine, add a second -L for it to the SSH command.
SSH gives you keys and encryption you already trust, and one tunnel covers a whole shelf. The cost is a host to run and an account on it, and anyone with that account controls every phone attached.
Way 2: SSH tunnel to adbd
Use this when a phone sits on a network you can reach through a jump host, and you have no USB access to a machine there. Put the phone in TCP mode once, over USB or wireless debugging, then tunnel to it:
adb tcpip 5555 ssh -N -L 15555:192.168.1.50:5555 user@jump-host adb connect 127.0.0.1:15555
Pick a local port outside 5555 to 5585, because the adb server treats local ports in that range as emulators. The phone must stay in TCP mode, which most builds drop after a reboot, and the first connection still shows the key dialog on the phone, so someone has to accept it once.
Way 3: Wireless debugging over a VPN
Android 11 added wireless debugging: pairing with a code, then a TLS connection. It is designed for a local network, but it works across a VPN such as WireGuard or Tailscale, as long as your laptop and the phone can reach each other's private addresses. Turn on Wireless debugging in Developer options, choose to pair with a pairing code, and run:
adb pair 100.64.0.12:37099 adb connect 100.64.0.12:41235
adb pair asks for the six-digit code. The pairing port and the connection port differ, and both change when wireless debugging is toggled. mDNS discovery does not cross most VPNs, so type the address shown on the phone. The setting also turns itself off when the phone leaves its Wi-Fi network, which makes it a poor fit for anything unattended.
Way 4: A device cloud relay
Device clouds put a relay in front of adbd and give each phone its own address. The details vary by provider. Here is how the relay works on Clousd, as documented in the ADB docs:
- ADB is off by default. You enable it per phone for an allow-list of up to 20 IP addresses or ranges.
- Access expires on its own: after 24 hours by default, 7 days at most. Idle sessions close after 30 minutes.
- You sign in with a one-time code that is shown once. Wrong codes close the session, and repeated failures switch the relay off.
- Root, remount, reverse, reboot and similar commands are always refused, and every session is logged.
adb connect HOST:PORT adb -s HOST:PORT shell clousd-login CODE adb -s HOST:PORT install app-debug.apk adb -s HOST:PORT logcat -d
There is nothing to host and nothing to open on your own network. The price is that a few commands are off the table. Workflows that rely on adb reverse, such as a React Native app reaching the Metro server on your laptop, need another path, for example a dev server the phone can reach by URL. If you do not have hardware to reach in the first place, a remote Android phone in the cloud comes with the relay already in front of it.
The four ways compared
| SSH to adb server | SSH to adbd | Wireless debugging over VPN | Device cloud relay | |
|---|---|---|---|---|
| Where the phone is | on USB at a host you control | on a network behind a jump host | on a network your VPN reaches | in the provider's data center |
| You maintain | the host and SSH access | the jump host and TCP mode | the VPN, the phone on Wi-Fi | nothing |
| Access control | SSH keys | SSH keys plus the adb key | VPN plus pairing | allow-list, expiry, one-time code |
| Survives a phone reboot | yes | usually not | no | yes |
adb reverse | yes | yes | yes | often refused |
| Good for | office racks, CI hosts | one-off reach into a lab | a personal phone | teams and CI without hardware |
Latency and bandwidth
adb is chatty but tolerant of distance. Interactive adb shell is usable at a 200 ms round trip, and logcat streams fine. Installs and pushes are bound by your upload speed, so a 150 MB APK over a slow home uplink takes minutes.
Instrumentation tests suffer most, because every UI action waits for a reply. Raise timeouts and replace fixed sleeps with waits for a condition, rather than adding longer sleeps. For screen mirroring with scrcpy through an SSH tunnel, lower the bitrate and size with scrcpy --video-bit-rate=2M --max-size=1024. On a cloud phone, the browser screen usually does that job.
Remote Android device testing in CI
A CI job that tests on remote phones has four steps: connect, wait for the device, run, disconnect. Add the sign-in step if your relay has one.
adb connect "$DEVICE" adb -s "$DEVICE" wait-for-device adb -s "$DEVICE" install -r app-debug.apk adb -s "$DEVICE" install -r app-debug-androidTest.apk adb -s "$DEVICE" shell am instrument -w com.example.app.test/androidx.test.runner.AndroidJUnitRunner adb disconnect "$DEVICE"
Keep device addresses and login codes in CI secrets, not in the repository. The app testing page shows how the same run looks through a REST API, and testing an Android app online covers the smoke pass and the logcat commands in detail. If you are deciding whether to keep your own rack at all, see Android device farm.
Security checklist
- Never expose ports 5037 or 5555 to the internet.
- Treat
~/.android/adbkeyas a credential. It is your identity to every phone that trusted it, so do not copy it to shared machines. - On lab phones, use Revoke USB debugging authorizations in Developer options from time to time.
- Prefer access that expires on its own and is limited to known addresses.
- Disconnect and stop the server when you are done with
adb disconnectandadb kill-server, and turn wireless debugging off. - Keep a record of who connected to which phone and when.


