May 24, 2026
Sub-second ADB: how we connect you to a phone over Tailscale
Most remote-device platforms make you wait fifteen seconds for a session to start. We get a working ADB shell to a physical phone in under a second. Here is the connect path.
When a customer clicks "Rent device" on DeviceRent, an ADB shell appears in their terminal in under a second. That number is on the homepage, and we get the same question every time someone reads it: how?
The honest answer is "we removed three things that other platforms put in the way." This is a walkthrough of what the connect path actually does, in the order it does it.
What we do not do
Before the steps, the omissions. We do not:
- spin up a VM,
- allocate a container,
- provision a TURN relay,
- negotiate a WebRTC offer,
- or wait for a userland ADB daemon to come up cold.
Every one of those is a multi-second cost that most cloud-device platforms eat because they treat each session as a fresh sandbox. We treat the device as a long-running resource that is always reachable, and we treat the session as a thin permission layer over the top.
Step 1 — the device is already on the network
Each phone in the rack is paired with a small controller that runs a Tailscale node. The node is up at all times; the device's ADB port is exposed only to the tailnet, never to the public internet. When we say "the device is online," we mean it has a stable WireGuard tunnel to our coordinator and the daemon is responding to a heartbeat.
That heartbeat is the only thing on the hot path that runs continuously. It costs nothing in latency at session start because the tunnel is already built — no handshake, no key exchange, no DNS round-trip.
Step 2 — the customer clicks Rent
The web app calls our backend, which does three things in one transaction:
- Confirms the customer has minutes available.
- Reserves the device row (Postgres SELECT … FOR UPDATE on
devices.id). - Mints a short-lived authentication token scoped to that one device for that one session.
This is roughly 20–40 ms total. Postgres is fast when the locked row count is one and the index is hot.
Step 3 — the customer's terminal connects
The customer's local ADB does not talk to our coordinator. It talks directly to the tailnet via their own Tailscale node — they ran one tailscale up during onboarding and have not thought about it since. The flow is:
customer adb client
→ Tailscale (their node)
→ Tailscale DERP or direct WireGuard
→ controller in our rack
→ USB → phone
That is the entire data path. There is no proxy in our datacenter. There is no service we run that sits in the middle of every keystroke. The packets go from the customer's laptop straight into the WireGuard tunnel and out the other side onto a USB cable two feet away from the phone.
Why this is fast
WireGuard handshakes are pre-built. ADB-over-TCP runs at userspace speed once the socket is open. The bottleneck is the customer's home internet, not us. From our coordinator's perspective, the work we do per session is: check auth, flip a row in Postgres, hand the customer a hostname. Total wall time on our side is under 50 ms. The rest is physics.
The "under one second" number is the time from the click to the first character at the prompt. We have seen 600–800 ms typical, 1.2 s on a bad day, and one outlier at 2.4 s when a customer's home router decided to renew its DHCP lease at the worst possible moment. We do not control that one.
What we gave up
This architecture has a real downside: we depend on Tailscale. If their coordination plane goes down, new tunnels cannot be established (existing ones keep working). We accept this because the alternative — running our own NAT-traversal stack — would mean either a relay (which adds 30–80 ms minimum) or a custom WireGuard-on-top-of-WebSocket fallback that takes a year to get right. Tailscale's uptime over the last twelve months has been better than what we could build ourselves.
We also gave up running ADB-over-WebSocket directly in the browser. We considered it. It is doable, and it is what some of our competitors do for their browser shells. We decided that anyone serious enough to want a remote Android device already has adb on their machine, and that running on the customer's real ADB is worth more than the convenience of a browser tab.
The point
Most "cloud" products have to be slow because their architecture forces a cold start. We do not have a cold start. The device is hot, the tunnel is hot, the auth is one query and one token. The session is just permission to talk to a port that was already listening.
That is the whole trick.