# Remote VM Setup

Pane can run your agents on a VM, WSL box, or any remote machine while you keep the UI on the device in front of you.

It works through a small <Term id="daemon">daemon</Term> on the <Term id="host">host machine</Term>. You connect with a `pane-remote://...` code from `Settings > Remote Access > Remote Pane` or the headless setup command. That's it.

This is a self-hosted <Term id="cloud-workspace">cloud workspace</Term> path when the host is a VM. You choose the machine, storage, credentials, and network path. Pane doesn't provide a public managed host.

Install your agent CLIs (Codex, Claude Code, etc.) on the remote host. Agents use that host's repos, shell, git config, env vars, and credentials.

<div style={{ margin: "2rem 0" }}>
  <RemotePanePreview />
</div>

## Two Pieces

There are two sides to Remote Pane:

1. **<Term id="host">Host machine</Term>**: the machine that owns the repos, terminals, worktrees, and agent processes.
2. **<Term id="client">Client device</Term>**: the laptop, desktop, phone, or tablet running the Pane UI that imports the connection code.

The host can be your Windows PC, a WSL distro, a Mac mini, a Linux box, or a cloud VM. The client can be the Pane desktop app or the browser app at [runpane.com/app](https://runpane.com/app/).

Remote Pane is free and open source. The daemon ships with Pane and you run it on your own hardware. There's no Pane cloud service or subscription. You bring the machine and the credentials.

## Host Credentials

Configure runtime credentials on the **host machine**, because that is where the daemon, terminals, worktrees, and agent processes run.

For coding agents:

- Install the agent CLIs on the host, such as `claude`, `codex`, `opencode`, `goose`, or `aider`.
- Configure each agent's auth on the host, either through the agent's login flow or shell environment variables such as `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `OPENROUTER_API_KEY`.
- See [AI providers](/docs/providers) for provider-specific env vars.

For PWA voice dictation:

1. Open Pane on the host machine.
2. Go to `Settings > Integrations > Voice transcription`.
3. Add the provider keys used by the remote browser app.

Live streaming voice uses **Deepgram** for realtime ASR and **OpenRouter** for transcript cleanup:

- `Deepgram API Key`
- `OpenRouter API Key`

Batch recorded voice uses **Fal** for Wizper transcription and **OpenRouter** for transcript cleanup:

- `Fal API Key`
- `OpenRouter API Key`

The daemon can also read these host environment variables: `DEEPGRAM_API_KEY`, `OPENROUTER_API_KEY`, and `FAL_KEY`. Settings are usually clearer for a remote daemon, especially when it runs as a background service.

## Recommended Setup

The easiest path is through Pane itself:

1. Install Pane normally on the machine that should host your repos and agents.
2. On that host machine, open `Settings > Remote Access > Remote Pane`.
3. Under `Set Up This Machine`, choose a data mode.
4. Choose `Tailscale`.
5. Click `Set Up Tailscale & Create Code`.
6. Copy the generated `pane-remote://...` connection code. If Pane offers `Open in Pane Terminal and Run Setup`, use it. The terminal can install Tailscale when needed, walk through login, configure Tailscale Serve, and create the code.

Paste that full code into the client Pane app:

1. Open `Settings`.
2. Expand `Remote Pane`.
3. Paste the code into `Connection Code`.
4. Click `Import & Connect`.

If the client device does not have Tailscale installed or cannot resolve the host's Tailscale hostname, Pane will show a recovery card. Use `Open in Pane Terminal and Run Setup` on the client, then retry the saved profile.

## Phone and Browser Access

The same connection code works in the Remote Pane browser app:

```text
https://runpane.com/app/
```

Use this when you want to check or steer terminal-backed sessions from a phone or tablet.

1. Set up the remote host with Tailscale or a trusted HTTPS tunnel.
2. Copy the full `pane-remote://...` code from `Settings > Remote Access > Remote Pane` on the host. If you used the headless installer, copy the code from the terminal output.
3. Open [runpane.com/app](https://runpane.com/app/) on the client device.
4. Paste the code and tap `Import & Connect`.

The browser app currently supports terminal-backed remote sessions. It's a <Term id="pwa">PWA</Term> you can install from a supported browser. The desktop app has the full Pane surface, including every panel type.

Read [what session persistence means](/what-is-session-persistence) before you plan unattended work. A persistent disk can keep your files while stopped processes are gone.

For mobile, prefer Tailscale or Manual HTTPS. SSH tunnel mode is best for desktop clients, because the browser must be able to reach the forwarded daemon URL from the device where it is running.

If your question is specifically whether Remote Pane works over SSH, see [Remote Pane over SSH](/docs/remote-pane-ssh). It explains the SSH tunnel flow, the exact setup commands, and why Pane uses a daemon instead of plain SSH.

### Install the Browser App

On iPhone or iPad:

1. Install and sign in to Tailscale if your connection code uses a Tailscale URL.
2. Open `https://runpane.com/app/` in Safari.
3. Tap the Share button.
4. Tap `Add to Home Screen`.
5. Open `Remote Pane` from the home screen and paste your connection code.

On Android:

1. Install and sign in to Tailscale if your connection code uses a Tailscale URL.
2. Open `https://runpane.com/app/` in Chrome.
3. Open the browser menu.
4. Tap `Add to Home screen` or `Install app`.
5. Open `Remote Pane` from the launcher and paste your connection code.

On desktop Chrome or Edge:

1. Open `https://runpane.com/app/`.
2. Click the install icon in the address bar, or use the browser menu and choose `Install app`.
3. Open the installed app and paste your connection code.

On desktop Safari:

1. Open `https://runpane.com/app/`.
2. Choose `File > Add to Dock`.
3. Open `Remote Pane` from the Dock and paste your connection code.

## Headless VM or Server Setup

For a headless VM or server, run the guided `runpane` CLI on the host and choose **Set up this machine as a remote host**:

<RemotePackageInstall />

The CLI setup flow installs Pane if needed, configures the remote daemon, configures Tailscale Serve when requested, and prints one `pane-remote://...` code.

If you want a direct command for automation:

```bash
npx --yes runpane@latest install daemon --label "My Server"
pnpm dlx runpane@latest install daemon --label "My Server"
pipx run runpane install daemon --label "My Server"
```

If Pane is already installed on the host, run setup directly:

```bash
pane --remote-setup --label "My Server"
```

If you are working from a source checkout instead of an installed build:

```bash
pnpm remote:setup -- --label "My Server"
```

## Check and Repair the Service

Check the isolated remote service before you restart it or reboot the host. If you chose a different directory during setup, use that path instead of `~/.pane_remote`.

```bash
runpane doctor --pane-dir ~/.pane_remote --json
```

Doctor checks two separate things:

1. Is the remote daemon reachable now?
2. Can its saved launcher find the installed Pane executable after a restart?

That second check matters after an upgrade. A Linux service can stay alive after its executable was deleted. It looks healthy, but its old launcher may fail on the next restart.

Doctor reports this as `PANE_REMOTE_DAEMON_EXECUTABLE_DELETED`. It also gives you the recovery command.

Repair the managed service:

```bash
runpane daemon repair --pane-dir ~/.pane_remote --yes --json
runpane doctor --pane-dir ~/.pane_remote --json
```

Repair rewrites the launcher and restarts the service. Connected clients will drop briefly. Your pairing and tunnel settings stay the same.

You can run repair again safely. The result tells you whether anything changed.

The repaired launcher finds the current Pane install after upgrades. On headless Linux, it also starts Pane with the flags it needs.

`runpane update` updates the desktop app. It doesn't migrate an old managed launcher. Use `runpane daemon repair` when doctor tells you the launcher is stale or unsafe to restart.

## Host Data Modes

Remote Pane supports two host modes:

### Current Pane Data

Use this when you want another device to control the same Pane app data that is already open on the host.

- Uses this Pane install's projects, sessions, and settings.
- The remote host is live while Pane is open on the host.
- Best for a desktop, WSL machine, or laptop you actively keep running.

### Isolated Daemon Data

Use this when you want a separate remote daemon data directory.

- Uses separate daemon data instead of the host app's current Pane data.
- Can install a background service where supported.
- Best for a VM, server, or always-on remote host.

## Tailscale Details

Pane keeps the daemon bound to loopback (`127.0.0.1`) by default. Tailscale Serve exposes that loopback daemon safely inside your tailnet.

```bash
tailscale serve --bg http://127.0.0.1:42137
```

Both devices must be signed into the same Tailscale account or tailnet. On macOS and Windows, the Tailscale app may need a moment after first login before MagicDNS and Serve are ready. If the profile times out immediately after setup, wait a minute or two and click `Retry Connection`.

The connection code may include a Tailscale hostname and a Tailscale IP fallback so Pane can still connect when the OS DNS resolver is slow to pick up MagicDNS.

## Networking Requirements

Here's the actual contract, no matter which tunnel you use.

The daemon listens on `127.0.0.1:42137` only. It never binds to a LAN or public interface. Every connection path is really just "something that forwards to that loopback port."

Tailscale is the default because setup is automated: it installs Tailscale, logs you in, and creates a Tailscale Serve HTTPS URL for you. It's not a requirement.

**Using Netbird, WireGuard, ZeroTier, or another VPN?** Pane has no built-in integration for any of them, but two patterns work today:

1. **Desktop client**: run an SSH tunnel over your VPN's IP address instead of the public internet. See [Fallbacks](#fallbacks) below.
2. **Any device, including phone**: put an HTTPS reverse proxy (Caddy or nginx) on the host, forward it to `127.0.0.1:42137`, then use Manual HTTPS mode with that URL. Any non-loopback URL must be `https://`.

If a remote connection fails, check these in order:

1. Run `runpane doctor --pane-dir ~/.pane_remote --json` on the host.
2. If doctor gives you a repair command, run it before you restart the service.
3. Check the tunnel. From the client, `curl` the URL saved in the Remote Pane profile. Any HTTP response means the tunnel reached Pane. A timeout or connection refusal means the tunnel is still down.
4. Make sure you pasted the whole `pane-remote://...` code. These codes are long and easy to truncate.
5. On Tailscale, make sure both devices are signed into the same tailnet.
6. Tailscale Serve can take a minute or two to finish setup.

## Host Controls

When this machine is accepting remote connections, `Settings > Remote Access > Remote Pane` shows a live host status.

From the host, you can:

- see whether the remote host is live
- see connected remote clients
- disconnect live clients
- revoke a paired client's access token
- stop the remote host

Disconnecting a client drops the current connection. Revoking a client removes its saved access, so that client cannot reconnect with the same imported profile.

The top of the sidebar shows which machine your agents run on: **This computer**, or the host's label. Use its menu to switch between saved hosts or go back to local. The sidebar also shows a `Remote` status dot. Green means this machine is hosting. Blue means this app is connected to a remote runtime.

## Multi-Client Device Labels

When several clients connect to the same host, the host lists each one by its device label in **Settings > Remote Access > Remote Pane**.

<DeviceLabels />

Each client is identified by its device label, set automatically from the connecting device.

## Permissions in Remote Mode

When an agent asks Pane for permission, the prompt goes to every connected client. Answer it from whichever device you're on. A prompt waits until someone answers it or the session is cleared. There's no timeout.

Most built-in agents start in their skip-the-prompts modes, so you'll rarely see these. See [Security](/docs/security#agent-permissions).


## Remote Desktop For GUI Apps

Pane controls the coding runtime: worktrees, terminals, Git actions, and AI agent panels run on the remote host.

For GUI work on the host, use Remote Desktop alongside Pane. This is the right path for:

- Electron apps
- native app windows
- browser previews that open on the host
- OS-level setup screens

When Pane is connected to a remote runtime, the sidebar shows a `Remote Desktop` shortcut that opens Chrome Remote Desktop. Set up Chrome Remote Desktop on the host if you want full visual access to that machine.

When connected to a remote runtime, the sidebar shows a Remote Desktop shortcut button that opens Chrome Remote Desktop directly.


## CLI Options

For a custom label:

```bash
npx --yes runpane@latest install daemon --label "GPU VM"
```

For SSH tunnel mode:

```bash
npx --yes runpane@latest install daemon --label "GPU VM" --prefer-tunnel ssh
```

For an already-installed Pane build:

```bash
pane --remote-setup --label "GPU VM" --prefer-tunnel ssh
```

For Python-only hosts:

```bash
pipx run runpane install daemon --label "GPU VM"
```

For a dry run:

```bash
npx --yes runpane@latest install daemon --label "GPU VM" --dry-run --verbose
```

<Toggle title="Advanced: shell installers">

For a validation or nightly build:

```bash
curl -fsSL https://runpane.com/install-remote.sh | sh -s -- --channel nightly
```

PowerShell users can run the installer and setup command together:

```powershell
& ([scriptblock]::Create((irm https://runpane.com/install-remote.ps1))) -Label "Windows VM"
```

For SSH tunnel mode on Windows:

```powershell
& ([scriptblock]::Create((irm https://runpane.com/install-remote.ps1))) -Label "Windows VM" -PreferTunnel ssh
```

</Toggle>

Useful setup flags:

- `--label "Name"`: set the host label shown in the client.
- `--pane-dir <path>`: choose the daemon data directory.
- `--listen-port 42137`: choose the local daemon port.
- `--auto-listen-port`: pick a nearby open port if the requested one is busy.
- `--prefer-tunnel tailscale`: prefer Tailscale Serve. This is the default and is the easiest path for phones.
- `--prefer-tunnel ssh`: print an SSH local-forward command and use a local `http://127.0.0.1:42137` profile.
- `--prefer-tunnel manual --base-url <url>`: use your own trusted HTTPS tunnel or reverse proxy.
- `--no-install-service`: skip background service installation.
- `--no-tailscale-serve`: skip Tailscale Serve setup.
- `--print-only`: print commands without applying setup. Use it with `--prefer-tunnel ssh` or `--prefer-tunnel manual --base-url <url>`; default Tailscale setup must configure Tailscale Serve before it can print a usable cross-device code.

## Fallbacks

If Tailscale is not available, use one of the advanced connection modes in Settings:

### SSH Tunnel

The setup output prints an SSH local-forward command:

```bash
ssh -N -L 42137:127.0.0.1:42137 user@your-host
```

Run that on the client machine before connecting the saved profile.

### Manual HTTPS

Use Manual HTTPS when you already have a trusted tunnel or reverse proxy that forwards to the host daemon.

Only expose the daemon through a trusted private tunnel or HTTPS endpoint. Do not expose the raw loopback daemon directly to the public internet.

## Security

The import code contains a bearer token. Treat it like a secret. If a code or client profile is shared accidentally, open `Settings > Remote Access > Remote Pane` on the host and revoke that client.
