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:

  1. SSH into the remote, authenticate normally. The remote-side mosh-server binary is launched, picks a free UDP port (~60000-61000 by default), and prints its UDP port + a one-time AES key.
  2. The local mosh-client reads 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, -D from 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-8 on 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.