Skip to main content
Teleoperation normally runs over your local network — the headset and the robot machine are on the same Wi-Fi. To drive the robot from anywhere over the internet, put both the Meta Quest and the robot machine on the same Tailscale network (a “tailnet”). The headset then reaches the robot by its Tailscale address exactly as if it were on the same LAN, with the traffic encrypted end-to-end. The only catch: Tailscale isn’t in the Meta Quest Store, so it has to be sideloaded. This guide walks through that, then the connection.
Remote teleop is sensitive to latency. Tailscale usually connects two devices directly (peer-to-peer); on networks where that isn’t possible it falls back to a relay, which adds latency. Expect the best results on connections with low round-trip time.

Sideload Tailscale on the Quest

Tailscale isn’t available on the Meta Quest Store, so you can’t install it the normal way. Since the Quest runs Horizon OS (an Android-based system), the workaround is to sideload Tailscale’s Android APK.
1

Enable Developer Mode on the Quest

  1. Open the Meta Quest mobile app on your phone, with your headset connected.
  2. Go to Menu → Devices → select your headset → Headset Settings → Developer Mode, and toggle it on. You may be prompted to create a free Meta “organization” first (takes a minute).
  3. Restart the headset.
2

Download the Tailscale APK

Download the Android APK from Tailscale’s package server at pkgs.tailscale.com/stable/#android onto your computer.
Only grab the APK from Tailscale’s own site — avoid random third-party APK mirrors.
3

Sideload it with SideQuest

The easiest tool is SideQuest:
  1. Install SideQuest on your computer from sidequestvr.com.
  2. Connect the Quest to your computer with a USB-C cable.
  3. Put the headset on and accept the Allow USB debugging? prompt (check Always allow from this computer).
  4. In SideQuest, use the Install APK from folder button and select the Tailscale APK.
Already have ADB set up? Skip SideQuest and run adb install tailscale.apk.
4

Launch and log in

Tailscale won’t appear in your normal app grid — it lives under the Unknown Sources section of your Quest app library (the folder icon with a ”?”). Open it, sign in to your tailnet, and approve the VPN connection prompt.
The Android client isn’t built for VR, so the in-headset login UI can be awkward. Many people find the auth-key / QR-code login flow easier than typing credentials in the headset.
Tailscale on the Quest gets no auto-updates this way, so you’ll periodically re-sideload a newer APK.

Connect the robot machine to the same tailnet

On the robot machine, install Tailscale and bring it up (see Tailscale’s Linux install docs):
Sign in with the same tailnet as the headset. Note the machine’s Tailscale address — its 100.x.y.z IP or its MagicDNS name (tailscale ip -4, or check the Tailscale admin console).

Drive the robot

With both devices on the tailnet, remote teleop is just teleoperation pointed at the Tailscale address instead of the LAN one:
1

Start teleop on the robot machine

Start it from the control panel or the CLI (axol teleop), exactly as for local teleop.
2

Make sure Tailscale is connected in the headset

Open the sideloaded Tailscale app on the Quest and confirm the VPN is active.
3

Enter the Tailscale address in the VR app

Open the VR app at axol.almond.bot on the headset and enter the robot machine’s Tailscale IP or MagicDNS name in the Connect field (instead of its LAN IP). Press Connect, then Start.
4

Authorize the certificate

As with local teleop, approve the self-signed certificate once — tap Authorize certificate when prompted, or visit https://<tailscale-address>:8000 in the headset browser and proceed past the warning. See Authorize the TLS certificate.
From there the session behaves exactly like local teleop — same controller layout, same camera views.

Drive from outside your tailnet (Funnel + TURN)

The flow above assumes the operator can join your tailnet. To let someone drive without installing Tailscale — reaching the robot over the raw public internet — expose it with Tailscale Funnel. This needs two pieces, because a tunnel only forwards the control connection, not the camera video.
Funnel puts the robot on the public internet, and the teleop connection has no authentication — anyone with the URL can drive the arms. Only expose it while you’re actively running a session, and take the Funnel down (sudo tailscale funnel --https=443 off) as soon as you’re done.

1. Expose the control connection with Funnel

Funnel only publishes on ports 443 / 8443 / 10000, and it terminates TLS itself, so point it at the VR server’s own HTTPS port and tell it to skip the self-signed cert check:
This serves the control connection at https://<your-machine>.tailXXXX.ts.net/ on 443 (check with tailscale serve status). Older Tailscale builds split this into two commands: tailscale serve --bg --https=443 https+insecure://localhost:8000 then tailscale funnel --bg 443 on.

2. Add a TURN server for camera video

Camera video is WebRTC, whose media is a separate peer-to-peer UDP stream — it does not travel through the Funnel. With both peers off each other’s network, they need a publicly reachable TURN relay to bridge the media. Use a hosted TURN provider, or self-host coturn on a box with a public IP. The repo ships a ready-to-deploy coturn config for Fly.io under deploy/turn/ — Fly supports the raw UDP that TURN needs (unlike most PaaS hosts), and the config handles the dedicated-IPv4 and external-ip gotchas for you. Follow deploy/turn/README.md to launch your own relay in a few flyctl commands, then point Axol at it with the env vars below. Set these env vars on the machine running teleop before starting it (the VR server and its video relay read them at connect time):
AXOL_TURN_URL is a comma-separated list of ICE URLs (turn:, turns:, or stun:). Without these, control still works over the Funnel but the camera feed stays blank for an off-tailnet operator. On a LAN or plain tailnet, leave them unset — the direct path is used as before.
Instead of exporting these every shell, drop them in a .env.local file at the repo root — axol loads it automatically at startup. It’s git-ignored, so your TURN credentials stay out of version control.

3. Connect from the headset

In the VR app’s Connect field, enter the Funnel endpoint as an https:// URL (or with an explicit :443) so the app targets the public port instead of the LAN default of 8000:
Funnel serves a valid public certificate, so there’s no self-signed cert to authorize. Press Connect, then Start.

Next steps

Teleoperation

The full teleop flow and controller layout.

VR Interface

The WebXR app, camera views, and connection details.