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.

Powered by Collected Notes