Skip to main content

Remote Development (SSH, Dev Container, WSL)

What This Chapter Covers​

This chapter explains how to open a folder that is not on your local machine as a workspace and work directly with the files, terminals, language servers, and build tools that live there. Three targets are supported: an SSH remote server, a Dev Container (Docker), and a WSL distribution.

Opening a remote folder does not change anything about how a project is structured or how model files are formatted — both are the same as on a local machine. This chapter is for developers who want to edit and build code on a target board or in a Linux build environment. The prerequisites differ per method: SSH needs a reachable server, Dev Container needs Docker installed and running locally, and WSL needs Windows with a Linux distribution installed. For how to use the code editor, the build commands, and the terminal themselves, see 16. Code Editing, Build, Terminal and Documents.

17.1 Entry Points​

Start a remote connection from the remote indicator in the status bar or from the command palette.

17.1.1 The Remote Indicator​

The leftmost item in the status bar is a >< indicator. Its appearance changes with the connection state.

StateDisplayColor
Not connectedIcon only. Tooltip Open a Remote WindowDefault
SSH connecting or reconnectingConnecting...Orange
SSH connectedSSH: <host label>Green background
SSH errorConnection ErrorRed
Container connectedContainer: <container name>Green background
WSL connectedWSL: <distro name>Green background

Click the indicator and a menu drops down from the top of the window. Its header reads Select an option to open a Remote Window. Press Esc or click outside to close it.

17.1.2 The Menu While Not Connected​

When you click the indicator while no connection is active, the following items appear.

ItemBadge on the rightAction
Connect to SSH Host...Remote-SSHOpens the SSH quick connect input (17.2.1)
Open Folder in Container...Dev ContainersBuilds a container from the devcontainer.json of a local folder and enters it
Add Dev Container Configuration Files...Dev ContainersCreates a devcontainer.json in a folder
Attach to Running Container...Dev ContainersConnects to a container that is already running
Connect to WSLRemote-WSLConnects to the default distribution, or to the first one in the list if there is no default
Connect to WSL using Distro...NoneLets you pick the distribution to connect to

The remote indicator menu while not connected

Clicking the >< indicator at the far left of the status bar drops this menu down from the top of the window. The header reads Select an option to open a Remote Window, and the badge on the right tells you which feature each item belongs to (Remote-SSH, Dev Containers, or Remote-WSL). Only Connect to WSL using Distro... at the bottom has no badge.

17.1.3 The Menu While Connected​

The items depend on the kind of connection.

Connection kindMenu items
SSHConnect to SSH Host... / Open Remote Folder... / Close Remote Connection
WSLOpen WSL Folder... / Connect to WSL using Distro... / Close Remote Connection
ContainerOpen Folder in Container... / Open Workspace in this Container... / Attach to Running Container... / (Rebuild Container) / (Reopen Folder Locally) / Close Remote Connection

Rebuild Container and Reopen Folder Locally appear only when you entered through Open Folder in Container.... A container connected through Attach to Running Container... has no known local folder of origin, so neither action applies.

17.1.4 The Command Palette​

The command palette carries SSH and WSL items only. There are no Dev Container items, so you must use the status bar indicator menu for those.

CategoryItems
Remote-SSHConnect to SSH Host... / Open Remote Folder... / Disconnect from SSH Host / Add New SSH Host...
WSLNew WSL Terminal / Open WSL Folder... / Connect to WSL...

New WSL Terminal does not merely open a terminal, despite its name, so take care. It behaves exactly like Connect to WSL...: it opens the distribution picker and then connects to the distribution you pick and reloads the window. If you are already connected to WSL and only need one more terminal, use the New Terminal button in the terminal panel.

Disconnect from SSH Host does more than drop the connection — it also clears the stored remote workspace. The next launch does not reconnect automatically.

The command palette filtered by Remote

Press Ctrl+Shift+P to open the command palette, then type Remote. Four items are grouped under the REMOTE-SSH category and, as the table above shows, the Dev Container items are not here. For those, use the status bar indicator menu.

17.2 SSH​

17.2.1 Quick Connect​

Selecting Connect to SSH Host... opens an input at the top of the window. It does not open the host manager directly.

  1. Type the connection details into the field and press Enter. The hint reads Enter SSH connection command (e.g. user@hostname, ssh user@host -p 2222). The forms user@host, user@host:2222, and ssh user@host -p 2222 are all parsed. If the form does not match, an Invalid Format dialog shows Use: user@hostname or user@hostname:port.
  2. You can also pick from the list below instead of typing. Saved hosts are listed under Saved Hosts with their label and user@host:port.
  3. Selecting + Add New SSH Host... or Configure SSH Hosts... at the bottom of the list opens the host manager (17.2.2).
  4. On a successful connection the window reloads, and if no workspace is open the remote folder browser opens automatically. The connection is live once SSH: <label> appears in the status bar.

When you connect by typing the details, they are saved automatically under a user@host label and appear in the list from then on. They are not stored in a PAIO-specific file but in your own ~/.ssh/config, and the entry is registered so that stored keys and ssh-agent are tried first (17.2.4).

The SSH quick connect input

Choosing Connect to SSH Host... opens this input first, not the host manager. The Saved Hosts list follows below the field, and under it sit + Add New SSH Host... and Configure SSH Hosts.... Either one takes you to the host manager.

17.2.2 The SSH Host Manager​

This is the dialog titled SSH Remote Hosts. Its elements behave as follows.

ElementAction
Reload ConfigRe-reads ~/.ssh/config
+ Add HostOpens the form for a new host
Connect on a hostConnects to that host
Pencil iconEdit
✕ iconDeletes the host, after a Delete this SSH host configuration? confirmation
Disconnect (at the bottom)Appears only while a connection is active

When no hosts are saved, No saved hosts. and Add a new host or import from SSH config. appear. While connecting, Connecting to <user>@<host>... is shown; on failure, Connection failed: <reason> appears together with a Back to Host List button.

The host list has exactly one source: ~/.ssh/config. The list is rebuilt by re-reading that file every time it is shown, and adding, editing, or deleting a host edits the Host block in that file directly. Editing the file by hand therefore takes effect without any separate import step, and conversely, deleting a host in PAIO removes its block from the file. Blocks that contain a wildcard, such as Host *, do not appear in the list.

The SSH Remote Hosts dialog

The dialog titled SSH Remote Hosts. Reload Config and + Add Host sit in the header, and below them the hosts read from ~/.ssh/config are listed with their label and user@host:port. The value in parentheses at the end is the authentication method, re-derived from the file contents.

17.2.3 Host Fields​

These are the fields you fill in when adding or editing a host.

FieldDefault / placeholderDescription
LabelEmpty (My Server)Display name. Leave it empty and it becomes user@host
HostEmpty (192.168.1.100 or hostname)IP address or hostname. Required
Port221 to 65535
UsernameEmpty (ubuntu)Account name. Required
AuthenticationPrivate KeyPrivate Key / Password / SSH Agent
Private Key PathEmpty (~/.ssh/id_rsa)Appears only when Private Key is selected
Remote Workspace Path (optional)Empty (/home/user/workspace)Even if you fill it in, nothing reads it (see the note below)

If Host or Username is empty, a Host is required or Username is required warning appears and nothing is saved. The save button reads Save when creating and Update when editing, with Cancel to its left.

The add host form

The screen after clicking + Add Host. The seven fields from the table above appear with their placeholders. Authentication is Private Key, so Private Key Path is shown alongside it, and because this is a new host the button reads Save.

Saving writes a Host block in ~/.ssh/config, so only what that file can express survives. Label, Host, Username, Port, and Private Key Path are recorded as Host, HostName, User, Port, and IdentityFile respectively (HostName is omitted when the label and the host are the same, and Port is omitted when the port is 22). The Authentication choice and the Remote Workspace Path, by contrast, are not recorded. When the list is re-read, Authentication is derived again from the file contents (SSH Agent if ForwardAgent yes is present, Private Key if an IdentityFile is present, and SSH Agent if neither is). In other words, saving with Password selected does not preserve that choice through the next refresh of the list.

Remote Workspace Path is not only unsaved — nothing reads it either. The folder to open after connecting is always chosen in the remote folder browser (17.5).

17.2.4 The Order Authentication Is Attempted​

Connecting through quick connect or by clicking an entry in the list follows this order.

  1. PAIO connects first with no credentials. At this stage the keys in ~/.ssh and ssh-agent are tried automatically.
  2. If that fails, PAIO asks for a password. The prompt reads <user>@<host>'s password:.
  3. If the password also fails, Failed to connect: <reason> appears.

Clicking Connect in the host manager takes a different path: it follows the stored Authentication value. With Password it prompts Enter password for <user>@<host>:, and with Private Key it prompts Enter passphrase for <key path> (leave empty if none):. Leave it empty if the key has no passphrase.

17.2.5 Host Key Verification​

PAIO records host key fingerprints in its own store. It neither reads nor writes OpenSSH's ~/.ssh/known_hosts, so even a server you have already reached from a terminal is treated as a new host by PAIO.

  • A host seen for the first time is accepted without any confirmation, and its fingerprint is recorded. The first connection is never blocked.
  • If the fingerprint changes on a later connection, PAIO refuses the connection and shows a Host Key Changed dialog. That means either a man-in-the-middle attack or a reinstalled server. Selecting Trust new key clears the stored fingerprint and connects once more; selecting Cancel lets the attempt fail.

17.2.6 Keep-Alive and Automatic Reconnection​

  • PAIO sends a keep-alive every 10 seconds and treats the connection as lost after three consecutive missed responses.
  • When the connection drops, PAIO reconnects automatically up to three times. The wait grows 1 second → 2 seconds → 4 seconds and never exceeds 10 seconds. Meanwhile the indicator reads Connecting....
  • After all three attempts fail, the state becomes Connection lost after max reconnect attempts.
  • On a successful reconnection, any open code editor restarts the remote language server automatically.
  • If reconnecting to the stored remote workspace fails when you restart the app, a Could not establish connection to '<host>' dialog appears, offering Retry or Open Folder....

17.2.7 Connection Failure Messages​

When a connection fails, PAIO rewrites the cause into one of the messages below. Anything that does not match is shown as the original text.

CauseMessage
Authentication failureAuthentication failed — check your SSH key, password, or that ssh-agent is running.
Connection refusedConnection refused — check the host address/port and that the SSH server (sshd) is running.
TimeoutConnection timed out — check your network, the host address, and any firewall.
Host not foundHost not found — check the hostname/address.
Host unreachableHost unreachable — check the network and that the host is online.
Handshake failureSSH handshake failed — the server may use an incompatible key exchange. (original text)

17.3 Dev Container (Docker)​

17.3.1 Prerequisites​

Docker must be installed and running locally. If it is not, a toast appears in the lower right corner and the action stops. The messages per state are as follows.

StateMessage
Docker not installedDocker is not installed. Install Docker Desktop to use Dev Containers.
Daemon not ready yetDocker Desktop is starting or not responding. Wait a moment and try again.

Container creation is performed by @devcontainers/cli, which ships with the app. You do not need to install it separately.

The toast shown when the Docker daemon is not ready

Choosing Open Folder in Container... with Docker installed but the daemon not running. The second message from the table above appears in the lower right corner and the action stops there. It is not a modal dialog, so you can continue with other work without closing it.

17.3.2 Open Folder in Container​

  1. Select Open Folder in Container....
  2. Pick a local folder in the folder dialog (Select Folder to Open in Container).
  3. PAIO looks for a configuration file in that folder. It checks two locations: <folder>/.devcontainer/devcontainer.json and <folder>/.devcontainer.json. If neither exists, a No Dev Container Configuration dialog shows This folder doesn't have a .devcontainer/devcontainer.json file. Create one first (or pick another folder). and the action stops.
  4. A Select a devcontainer.json file picker opens so you can confirm the file that was found. Cancelling here stops the process. This step is skipped when you arrive through the automatic prompt (17.3.7).
  5. A progress toast appears in the lower right corner and the container is built (17.3.3).
  6. On completion PAIO connects to the container and reloads the window. The workspace is the folder reported by devcontainer, or /workspaces/<folder name> if that is unknown. One terminal also opens automatically inside the container.
  7. The connection succeeded once Container: <container name> appears in the status bar.

17.3.3 The Progress Toast​

Progress is shown in a toast in the lower right corner that does not cover the window. Because it is not a modal dialog, you can keep working while it runs. The titles per stage are as follows.

StageTitle shown
StartSetting up dev container
Pulling the base imagePulling base image
Building the imageBuilding image
Installing featuresInstalling features
Starting the containerStarting container
Running postCreateCommandRunning postCreateCommand
Wrapping upFinalizing
DoneContainer ready
FailedDev container failed
CancelledCancelled
  • Clicking Show log expands the last 50 lines of the log and the button becomes Hide log.
  • Clicking Cancel changes the button to Cancelling... and stops the work in progress.
  • On success the toast shows Container ready briefly and then disappears on its own.
  • On failure the log expands automatically and is replaced by the full build log, and the Cancel button becomes Close. A Dev Container Failed dialog then shows the cause.

17.3.4 Attach to Running Container​

  1. Select Attach to Running Container....
  2. Pick a container from the Select a running container to attach list. Each entry shows the name, the state, and the image. Only running containers appear; if there are none, a No running containers found! toast appears.
  3. Once connected, the window reloads and the folder browser for the container opens. One terminal also opens automatically.

17.3.5 Add Dev Container Configuration Files​

This wizard creates a new .devcontainer/devcontainer.json.

  1. Select Add Dev Container Configuration Files.... If the current workspace is remote (a UNC path or docker://), a Local Folder Required dialog shows Adding a Dev Container configuration only works on local folders. Reopen Folder Locally first. and the action stops.
  2. Pick a template from the Add Dev Container Configuration Files — select a template (C/C++ / Rust) list.
TemplateDescription (as shown)What it creates
C/C++Ubuntu 22.04 + gcc / g++ / clang / cmake / gdb / lldb (apt install)Named C/C++ Dev Container, with a postCreateCommand that installs build-essential gcc g++ clang cmake gdb lldb pkg-config
RustUbuntu 22.04 + rustup / cargo / rustc (devcontainer feature)Named Rust Dev Container, with the ghcr.io/devcontainers/features/rust:1 feature
C/C++ + RustUbuntu 22.04 + both of the above (paio recommended — for SDV/automotive work)Named C/C++/Rust Dev Container, including both of the above

The template list in the Dev Container configuration wizard

The template list from step 2. The three entries from the table above each appear with their description, and choosing a folder comes in the next step. The header still reads (C/C++ / Rust) even though there are three templates.

  1. If a workspace is open, the file is created in that folder; otherwise it goes into the folder you pick in the folder dialog.
  2. If the file already exists, an Overwrite Existing Configuration? confirmation appears. Select Overwrite to replace it.
  3. Once the file is written, a prompt asking whether to reopen the folder in a container appears in the lower right corner right away (17.3.7).

All three templates use mcr.microsoft.com/devcontainers/base:ubuntu-22.04 as the base image and vscode as the container user. PAIO is an IDE for the SDV and automotive domain, so it offers C/C++ and Rust templates only.

17.3.6 Rebuild Container​

This action applies changes you made to devcontainer.json. It appears in the menu only when you entered through Open Folder in Container....

  1. Selecting Rebuild Container shows a confirmation dialog. It explains that the existing container is removed and rebuilt, and that any change outside the bind-mounted workspace folder — manually installed packages, for example — is lost. Click Rebuild to proceed.
  2. The existing connection is torn down. The language server, the terminals, and the file watcher go down with it.
  3. The progress toast appears. It looks the same as during the first build; only the title differs, starting with Rebuilding <name>.
  4. On completion PAIO connects to the new container and re-enters the workspace. On failure a Rebuild Failed dialog appears.

Running the action while the menu item is not available shows a Cannot Rebuild notice. A container connected with Attach does not track a devcontainer.json, so you have to rebuild it directly with the docker CLI.

17.3.7 Reopen Folder Locally and the Automatic Prompt​

Reopen Folder Locally drops the container connection and reopens the original local folder. This item, too, appears only when you entered through Open Folder in Container....

Separately, opening a local folder that contains a .devcontainer as a workspace shows a prompt automatically in the lower right corner.

This folder contains a Dev Container configuration.
Reopen in container "<name>"?

The buttons in the prompt behave as follows.

ButtonAction
Reopen in ContainerEnters the container creation process. The automatic prompt is not shown again for this folder
Don't show againStops asking for this folder
DismissCloses the prompt this time only

The automatic prompt shown when opening a folder that contains .devcontainer

The automatic prompt shown in the lower right corner. The quotes on the second line hold the name from devcontainer.json, and the three buttons from the table above appear as listed.

This prompt is not shown when you are already in remote, container, or WSL mode.

17.3.8 When the Container Stops from Outside​

If the container stops outside PAIO — through docker stop, for example — the connection state is cleared, the status bar item disappears, and the toast below appears. To use the container again, reopen the folder in a container.

Disconnected — the <name> container has stopped. Reopen the folder to reconnect.

17.4 WSL (Windows)​

There are three WSL items.

ItemAction
Connect to WSLConnects to the default distribution, or to the first one in the list if there is no default
Connect to WSL using Distro...Lets you pick a distribution from the Select a WSL distribution to connect to list. The default distribution is marked default
Open WSL Folder...Opens a folder inside a distribution. If a connection is already active it uses that distribution; otherwise it uses the default one

The WSL distribution picker

The list that opens when you choose Connect to WSL using Distro.... The header reads Select a WSL distribution to connect to, and only the default distribution carries a default marker on the right.

Both connection items follow the same sequence.

  1. The distribution is determined.
  2. The window reloads.
  3. After the reload, the folder browser for the distribution opens automatically. The starting path is $HOME inside the distribution, or /home if that lookup fails.
  4. One terminal opens automatically inside the distribution.
  5. Picking a folder opens the workspace as wsl://<distro><path>, and the status bar shows WSL: <distro>.

If WSL is missing or no distribution is installed, a WSL Not Available dialog shows the notice below.

WSL is not available, or no Linux distribution is installed.

Install WSL: wsl --install
Install a distro: wsl --install -d Ubuntu

A workspace path of the form \\wsl$\... or \\wsl.localhost\... is recognized as a WSL connection without any separate connection step, and the distribution name appears in the status bar.

17.5 The Remote Folder Browser (Common)​

SSH, WSL, and containers all use the same folder browser. It drops down from the top of the window and only the header differs (Open Remote Folder (SSH), Open WSL Folder (<distro>), Open Folder in Container (<name>)). Its elements behave as follows.

ElementAction
Eye icon (Hide dot files / Show dot files)Hides or shows entries that start with a dot. All entries are shown by default
Path fieldType a path and press Enter to open that path
OKOpens the directory you are currently viewing as the workspace
Show LocalOpens the local folder dialog so you can return to a local workspace
..Moves to the parent directory. One click is enough
Folder entryA single click enters that folder

The list shows folders only. Files do not appear. If a path cannot be read, Failed to browse: <reason> appears. Press Esc or click outside to close the browser.

The remote folder browser showing the inside of a WSL distribution

How it looks when opened for WSL. The header changes to Open WSL Folder (<distro>), and the eye icon sits at the far right. The starting path is $HOME inside the distribution, and the .. at the top of the list moves you to the parent. You can also see that only folders appear, with no files.

The position shown first depends on the case.

CaseStarting path
SSH/home
WSL$HOME inside the distribution. /home if that lookup fails
ContainerThe container's working directory (workdir). / if there is none

Choosing to open a folder while no connection is active shows a notice dialog and stops. For SSH, a Not Connected dialog shows Connect to an SSH host first.; for containers, a Not Attached to a Container dialog shows Attach to a container first..

17.6 Remote Language Servers and Build Tools​

17.6.1 Automatic Deployment​

When you open or build a C/C++/Rust file in a remote, container, or WSL workspace, PAIO uploads the Linux x64 binaries it ships with to the remote environment and runs them there. You do not need to install anything on the remote yourself. The tools that are deployed and where they land on the remote are as follows.

Deployed toolInstall location on the remote
clangd~/.paio-server/bin/clangd
rust-analyzer~/.paio-server/bin/rust-analyzer
CMake~/.paio-server/cmake/
gcc / g++~/.paio-server/gcc/
Rust toolchain (cargo, rustc)~/.paio-server/rust/
  • If the same version is already uploaded, PAIO does not upload it again.
  • Language server upload progress appears in a banner in the lower right corner. While it runs you see Deploying clangd to remote… (42%); when it finishes, Remote LSP ready appears briefly and disappears. On failure, Remote LSP: <tool> deploy failed (<reason>) stays for a few seconds.
  • If the CMake, gcc, or Rust deployment fails, PAIO falls back to the remote system's cmake, gcc, or cargo. If those are missing too, the build output carries an installation notice (see 16.2.4).

17.6.2 Supported Environments​

The bundled binaries are built for glibc-based Linux x86_64. The following environments are not supported.

ConditionResult
Anything other than Linux x86_64 (arm64, macOS, and so on)Deployment refused
musl-based distributions (Alpine and so on)Deployment refused

When an SSH remote is outside the supported range, the warning toast below appears once per workspace.

This remote server is not a supported environment (e.g. arm64, Alpine/musl, or non-Linux), so code intelligence (completion / diagnostics) is unavailable. PAIO bundles language servers for glibc Linux x64 remotes only.

When a WSL distribution is outside the supported range, language server deployment stops with WSL distro is not x86_64 — only Linux x64 is supported or WSL distro uses musl (Alpine) — only glibc is supported. CMake, gcc, and Rust toolchain deployment stops for the same reason, with slightly shorter wording.

Using Rust inside a container requires cargo. PAIO first tries to deploy its own Rust toolchain, and if that is unavailable it looks for a cargo already installed in the container. rust-analyzer starts if either one is present.

If none of the three is present, rust-analyzer does not start. No notice appears on screen and the state simply stays stopped, so when Rust completion silently does nothing in a container, check first whether cargo is installed inside the container (see 16.1.3).

17.7 Closing the Connection​

Selecting Close Remote Connection drops the connection, closes the open remote workspace, and reloads the window. You return to the local state. All three methods also clear the stored remote workspace, so the next launch does not reconnect automatically. Editor tabs that are dirty are saved first while the workspace closes.

17.8 Limits and Constraints​

These are the limitations worth knowing before you use remote development.

  • The window reloads every time you connect or disconnect. Save any unsaved edits beforehand.
  • Port forwarding is not provided.
  • A file change on the remote may show up in the window late, because the detection method varies by environment. When it lags, use the Refresh Explorer button at the top of the Explorer.
  • The bundled toolchain paths PAIO prepends to PATH are host paths, so they are of no use in a remote terminal. They are not applied to an SSH shell at all (see 16.3.3). Build tools used on the remote follow the automatic deployment paths in 17.6.
  • A remote build is killed after 120 seconds.
  • The container features depend on the local Docker. If Docker Desktop is not running, no container item proceeds.
  • One window holds one remote mode at a time. You cannot connect to SSH, a container, and WSL simultaneously.