Thin clients for AI coding agents
I have a MacBook Pro at home that runs Docker, my development environments, AI tooling, browsers, and databases. I wanted to reach it from anywhere using an iPad on cellular — without exposing ports to the internet, running a VPN appliance, or paying for a hosted workstation.
The setup took under an hour and now handles most of my remote development.
The architecture
- Tailscale — private networking
- Termius — SSH client on the iPad
- tmux — persistent sessions
- Screens (optional) — full desktop when needed
iPad (cellular)
│
├── Tailscale
├── Termius
└── Screens (optional)
│
▼
MacBook Pro at home
├── Tailscale
├── SSH
├── tmux
└── Docker
The Mac stays the source of truth. The iPad is a thin client with good battery life and always-on connectivity.
Tailscale
Every device joins a private network, so there’s no port forwarding, no router config, no dynamic DNS, and no SSH exposed to the public internet.
The Mac gets a stable hostname like host.tailnet.ts.net, and the iPad connects as if it were on the same LAN.
SSH instead of remote desktop
Most development work doesn’t need a desktop. Over SSH I manage Docker containers, tail logs, deploy code, run Git, and use AI coding tools. The connection is fast and uses little bandwidth.
tmux
I keep one persistent session behind an alias:
alias work='tmux new -A -s wo / rk'
Every login is then just:
work
If the session exists, it reconnects; if not, it’s created. Builds, logs, and long-running scripts keep going when cellular drops, Wi-Fi changes, the iPad sleeps, or SSH disconnects.
Locking down SSH
Once key authentication worked, I disabled password authentication. With Tailscale, that leaves no exposed SSH port, no public IP access, and key-based auth only.
One detail that tutorials skip: key auth fails if ~/.ssh/authorized_keys doesn’t exist or has the wrong permissions. Mine didn’t exist, so the handshake completed but authentication failed.
On the Mac:
touch ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
Paste your public key (the ssh-ed25519 AAAA... line) into that file. If a login still fails, watch the log while you retry:
sudo log stream --predicate 'process == "sshd"'
You’ll get a specific reason — Failed publickey, Authentication refused: bad ownership, etc. — instead of guessing. Confirm key auth works before disabling passwords, not after.
Docker from anywhere
docker ps
docker logs -f api
docker compose up -d
Browser-based tools — Portainer, Grafana, Open WebUI, internal dashboards — I open in Safari over the Tailscale network.
When I need the desktop
For Finder, Xcode, browser debugging, Docker Desktop, or other GUI-only apps, I use Screens. It handles the occasional admin task, but I reach for it rarely. SSH is the default.
Notes
The iPad doesn’t replace the Mac; it’s more useful paired with a machine that already exists. No cloud-workstation fees, no infrastructure to maintain, and secure access from anywhere.