Remote development — work on another machine via SSH · Tailscale · VS Code Remote
Drive your home desktop / home server / cloud workstation from a laptop seamlessly — Tailscale · SSH · VS Code Remote · tmux unified setup.
"Keep the powerful desktop at home and travel light" or "use a GPU workstation remotely" — same scenario. Core tools: Tailscale (zero-config VPN) + SSH + VS Code Remote-SSH + tmux. Set up once and a cafe feels like your desktop.
I think what Tailscale changes about remote development is the barrier to entry. Not because SSH was hard before — it wasn't — but rather because NAT traversal and VPN configuration used to require either a static IP, a VPN server to maintain, or exposing SSH to the public internet. Because Tailscale handles the networking layer automatically, the setup reduces to "install on both machines and sign in," which is what makes the remote-desktop pattern accessible to anyone.
TL;DR
- Tailscale — mesh VPN that punches NAT/firewalls. Connect machines without a corporate VPN
- SSH — all traffic after auth
- VS Code Remote-SSH — edit remote files locally as if they were local
- tmux — session persistence. Work survives disconnects
- Mosh (optional) — SSH alternative stable on mobile networks
Prerequisites
- SSH + accounts + internet on both ends
- (Optional) No need for a domain or static IP — Tailscale handles it
1. Tailscale Setup
Tailscale is a WireGuard-based mesh VPN. Install on each machine + log into the same account → private IPs (100.x.x.x) are assigned. NAT punched, direct connect.
1.1 Install
Mac:
brew install --cask tailscaleWindows:
winget install --id tailscale.tailscaleLinux (Ubuntu):
curl -fsSL https://tailscale.com/install.sh | sh1.2 Sign In
On each machine:
sudo tailscale up # Mac/Linux
# Or use the GUI tray → LoginOpens a browser → sign up with Google/GitHub/Microsoft (Tailscale free: 100 devices).
1.3 Find the Machine IP
tailscale ip -4
# 100.x.x.xOr see all machines at admin.tailscale.com.
1.4 Machine Names (MagicDNS)
Admin console → DNS → enable MagicDNS. Now SSH by name like desktop.tail-scale.ts.net:
ssh me@desktop
# (.tail-scale.ts.net auto-appended)Once enabled, never think about private IPs again. The rest of this guide assumes the name
desktop.
2. SSH Prep
2.1 Enable SSH on the Remote
Mac (remote):
- System Settings → General → Sharing → Remote Login ON
Windows (remote):
- Settings → System → Optional features → install OpenSSH Server
- PowerShell (admin):
Set-Service -Name sshd -StartupType Automatic Start-Service sshd
Linux (remote):
sudo apt install openssh-server
sudo systemctl enable --now ssh2.2 Register the SSH Key
Add the local public key to the remote's ~/.ssh/authorized_keys:
# Local
ssh-copy-id me@desktop
# Or manually on the remote:
mkdir -p ~/.ssh && chmod 700 ~/.ssh
echo "ssh-ed25519 AAAA..." >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys2.3 Connect
ssh me@desktop
# Or directly to 100.x.x.x3. VS Code Remote-SSH
Edit remote files at local speed from VS Code.
3.1 Install the Extension
VS Code marketplace:
- Remote - SSH (
ms-vscode-remote.remote-ssh)
Or:
code --install-extension ms-vscode-remote.remote-ssh3.2 Connect
VS Code Command Palette (⌘⇧P / Ctrl+Shift+P) → Remote-SSH: Connect to Host → enter me@desktop.
First connect installs VS Code Server on the remote (~50MB, once). Subsequent connects are instant.
3.3 Use
Bottom-left shows SSH: desktop. File → Open Folder → open ~/work/repo on the remote. Extensions install on the remote (e.g., the Python extension uses the remote's Python env).
3.4 Recommended Extensions (remote)
- ESLint / Prettier
- Python / Pylance
- Docker
- (Keep theme/keymap on local for neatness)
4. tmux — Session Persistence
If SSH drops, work stays:
4.1 Install (remote)
# Mac / Linux remote
brew install tmux # Mac
sudo apt install tmux # Ubuntu4.2 Use
ssh me@desktop
tmux new -s work # start session
# ...work...
# Ctrl+B, D → detach (session keeps running)
# Later
ssh me@desktop
tmux attach -t work # reattach4.3 Key Shortcuts
Shortcut (prefix = Ctrl+B) | Action |
|---|---|
prefix + D | Detach |
prefix + C | New window |
prefix + N / P | Next / previous window |
prefix + % | Vertical split |
prefix + " | Horizontal split |
prefix + arrows | Move pane |
prefix + Z | Zoom pane |
4.4 Auto-attach
.zshrc (remote):
if [[ -z "$TMUX" && -n "$SSH_CONNECTION" ]]; then
tmux attach -t main || tmux new -s main
fiSSH in → auto-attach to main (or create).
5. Mosh — On Mobile Networks
If SSH drops a lot during transitions (5G ↔ Wi-Fi), use Mosh (Mobile shell). UDP-based, local echo, auto-recover.
brew install mosh
# Remote
sudo apt install moshmosh me@desktopUses SSH for auth + UDP ports 60000-61000. No extra firewall configuration if going through Tailscale.
6. Scenario: GPU Workstation Remote
Goal: train ML on desktop GPU from a laptop.
# On the laptop
ssh me@gpu-workstation
tmux new -s train
# Inside tmux
cd ~/projects/ml
python train.py --epochs 100
# Ctrl+B, D to detach
# Close the laptop, move to a cafe
# Reconnect
ssh me@gpu-workstation
tmux attach -t train
# Training continuesEdit with VS Code Remote + run training in tmux. Both run together.
7. Security
Tailscale ACL (optional)
By default, all machines on the same account see each other. For corp/family mix:
- Admin console → ACLs → JSON policy
- Tag-based permissions (
tag:dev,tag:home)
Harden SSH
On the remote /etc/ssh/sshd_config:
PasswordAuthentication no # keys only
PermitRootLogin no
AllowUsers me
sudo systemctl restart sshd1Password SSH Agent
See password manager. With Tailscale + 1Password agent, SSH needs only biometric — no passphrase.
Verification
tailscale statuson both → "online"ssh me@desktop→ instant login (one passphrase or biometric)- VS Code Command Palette → Remote-SSH Connect → edit + save → changes reflect on the remote
tmux new -s test→ detach →tmux attach -t test→ state preservedmosh me@desktop→ drop Wi-Fi, switch networks → session auto-recovers
Troubleshooting
Tailscale doesn't connect
- Both machines run
tailscale upand signed in - Admin console shows both as "Connected"
- Firewall — Tailscale uses 41641/UDP. Corp networks may block (falls back to DERP relay)
ssh: connect to host 100.x.x.x: Connection refused
- SSH service isn't running on the remote (re-check §2.1)
- Windows:
Get-Service sshdis Running?
VS Code Remote is slow
- First connect installs Server — slow once
- If going via internet (Tailscale Direct failed) —
tailscale ping desktopto see direct vs DERP
Colors broken in tmux
.tmux.conf:
set -g default-terminal "screen-256color"
set -ga terminal-overrides ",xterm-256color:Tc"
Mosh won't connect
UDP 60000-61000 blocked — common on corporate networks. Fall back to SSH. Mosh needs UDP.
SSH agent forwarding (be careful)
For SSH-from-remote-to-another-remote, ssh -A. Only when the intermediate remote is trusted — a malicious root would see keys.
References
- Windows initial setup / Mac initial setup — prerequisites
- Windows git auth / password manager — SSH key management
- Tailscale (official)
- VS Code Remote-SSH
- Mosh
Changelog
- 2026-05-12: First draft. Tailscale · SSH · VS Code Remote · tmux · Mosh + scenario · security + six troubleshooting cases.
Keep reading
- Dev Container — A unified Mac / Windows / Linux dev environment
Use VS Code Dev Containers to erase OS differences. A Docker-based, reproducible dev env defined by a single devcontainer.json the whole team shares.
- Mac ↔ Windows keyboard mapping — Karabiner + PowerToys Keyboard Manager
Smooth out the Cmd ↔ Ctrl divide between the two OSes. Karabiner-Elements (Mac) and PowerToys Keyboard Manager (Windows) setup plus five common remappings.
- Mac + Windows clipboard sync — Universal Clipboard · 1Clipboard · self-hosted
Three paths to sync text/images between Mac and Windows clipboards instantly. Free, paid, and self-hosted compared.