What it solves
- Roaming. Close the laptop, move to another network, open the laptop — the session is still there, no reconnect needed.
- Lossy links. Over high-latency or packet-loss Wi-Fi, SSH feels sluggish (every keystroke is a round-trip). Mosh predicts the local echo and shows it underlined until confirmed, so editing feels instant.
- Tablet / phone keyboards. Mosh apps on iOS (Blink Shell, Termius) and Android (Termux + mosh) handle the mobile use case much better than raw SSH.
How it works
Two-step handshake:
- SSH into the remote, authenticate normally. The remote-side
mosh-serverbinary is launched, picks a free UDP port (~60000-61000 by default), and prints its UDP port + a one-time AES key. - The local
mosh-clientreads that and disconnects the SSH session. From then on, the persistent connection is UDP-only, using SSP (State Synchronization Protocol) instead of an interactive byte stream.
SSP works on screen state, not byte stream: the client sends keypresses, the server sends back the diff of the screen since last ack. Lost packets just cause a slightly delayed screen refresh, not a stalled terminal.
Install
On both ends:
# Debian / Ubuntu
sudo apt install mosh
# macOS
brew install mobile-shell
# Arch
sudo pacman -S mosh
# Fedora
sudo dnf install mosh
iOS: Blink Shell or ShellFish. Android: Termux + pkg install mosh.
Open the UDP ports
On the server, open UDP 60000–61000 in whatever firewall the host runs (or limit to the range Mosh actually uses):
# UFW
sudo ufw allow 60000:61000/udp
# nftables
sudo nft 'add rule inet filter input udp dport 60000-61000 accept'
# firewalld
sudo firewall-cmd --permanent --add-port=60000-61000/udp
sudo firewall-cmd --reload
If on a NAT'd cloud VPS or behind a corporate firewall, also open the same range in any cloud security group / corporate ACL.
Connect
mosh user@host.example.com
That's it. Mosh's binary wraps SSH for auth (any ~/.ssh/config aliases work), so:
mosh my-server # alias from ~/.ssh/config
mosh -P 2222 user@host # custom SSH port
mosh --ssh="ssh -i ~/.ssh/special_key" user@host
tmux / zellij behind mosh
Mosh sessions persist over network failures but don't survive server reboots or local terminal closes. For "persistent across the actual session," pair with a multiplexer:
mosh user@host -- tmux new -A -s main
# -A: attach to existing session, create if missing
# -s main: session named "main"
Now the workflow is: mosh + tmux reconnects to the same tmux session across network blips, reboots, and laptop swaps.
Worth knowing
- Mosh doesn't do port forwarding.
-L,-R,-Dfrom SSH have no equivalent. If you need a tunnel, open an SSH session in parallel. - Mosh doesn't do scp / rsync. File transfer is still SSH-based; mosh is for the interactive shell only.
- UTF-8 only. Mosh assumes the client and server agree on UTF-8 locale; old C-locale logins won't render right.
LANG=en_US.UTF-8on both ends. - Server's clock matters. Mosh uses the server clock for state-synchronization timeouts; a wildly-off clock causes weird behavior. Pair with chrony (see that tutorial).
Detect & debug
# Verbose mode
mosh --verbose user@host
# Confirm mosh-server is running on the remote after connect
ssh user@host pgrep -af mosh-server
# Check the UDP port that ended up in use
ss -ulpn | grep mosh-server
When to stick with SSH
- Anything scripted (Ansible, scp, rsync, CI jobs) — SSH is the protocol those tools speak.
- When the network is reliable and you need port forwarding.
- For non-Latin-script or unusual terminal needs — mosh's screen-diff protocol assumes a fairly standard terminal model.
For an interactive shell on a laptop that crosses networks, mosh is the better default. For everything else, OpenSSH (see that tutorial) stays.