Skip to content

SOPs

Mosh — Resilient SSH

Use mosh for interactive terminal sessions across my machines. Use ssh for everything non-interactive (scripts, one-shot commands, rsync, git).

Mosh where drops hurt, SSH where scripting matters.

Terminal window
brew install mosh

Version 1.4.0 as of 2026-04-24. Both ends need it, the local machine and the remote.

Installed on: MacBook Pro, Mac Studio. Not yet on: Remote Mac, VPS.

Terminal window
mosh studio # → Mac Studio
mosh mbp # → MacBook Pro

SSH aliases in ~/.ssh/config resolve the same way they do for ssh. The terminal title shows something like mosh-client -# studio | 192.168.1.220 60001 when the session is live. The trailing number is the UDP port mosh bound to.

  1. Mosh SSHes in to launch mosh-server on the remote and receive a one-time key plus a UDP port.
  2. The session then switches to UDP (ports 60000 to 61000) using that key.
  3. UDP survives IP changes, sleep, and Wi-Fi flips. SSH does not.

The SSH bootstrap is where the tricky behavior lives. Non-interactive SSH shells on macOS behave differently from interactive ones, which leads directly to the gotcha below.

Symptom. First mosh studio from MacBook Pro returned:

zsh:1: command not found: mosh-server
/opt/homebrew/bin/mosh: Did not find mosh server startup message.

Cause. Mosh’s SSH bootstrap runs a non-interactive, non-login zsh on the remote. That shell does not source .zshrc or .zprofile, which is where Homebrew’s PATH export normally lives. So mosh-server at /opt/homebrew/bin/mosh-server is not on PATH, the bootstrap fails, and mosh reports “did not find startup message.”

Fix. Write a one-line ~/.zshenv on every machine mosh reaches:

Terminal window
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:$PATH"

.zshenv is the only zsh init file that loads for every invocation: interactive, non-interactive, login, non-login. It is the right hook for anything an SSH-bootstrapped tool needs on PATH.

Mosh uses UDP 60000 to 61000. On both the MacBook Pro and Mac Studio the macOS Application Firewall is disabled (socketfilterfw --getglobalstate reports “Firewall is disabled”), so nothing to configure. If a firewall ever gets enabled, either allow mosh-server as an app or open UDP 60000 to 61000.

2026-04-24. VS Code Remote SSH sessions from MacBook Pro into Mac Studio were dying with Claude Code process exited with code 143 (SIGTERM). Root cause was SSH ControlMaster tearing down on brief network blips and taking the remote shell, plus the Claude Code process, with it. Installed mosh on both ends to eliminate the wobble. First launch hit the .zshenv PATH gotcha above. Fixed, retested, documented here.

  • New machine joins the fleet → brew install mosh, write .zshenv, add a line under “Installed on” in the Install section.
  • Firewall state changes on any machine → update the Firewall section.
  • Future mosh versions change behavior → update the Install section and add a line under Precedent.
  • Local reference with a shorter version: ~/apps/cc/infrastructure-guide.md
  • Mosh homepage