I recently set up my first VPS to deploy a small web app. The setup went fine. But after leaving an SSH session open overnight, I came back to find the terminal was frozen. I couldn't type anything, and Ctrl+C did nothing.

This post explains why that happens and two tools that can help: SSH keepalives and tmux.

Why the terminal froze

When an SSH connection carries no traffic for an extended period, a router or NAT device between you and the server may assume the connection is dead and drop it. Neither your computer nor the server is notified. Your local ssh process still thinks it is connected, so every key you press is sent over a connection that no longer exists.

Ctrl+C doesn't help either. Normally, Ctrl+C sends a SIGINT signal to the foreground process through the terminal. But when the SSH connection is already dead, that signal can't reach the server, so nothing happens.

How to recover

SSH has escape sequences that communicate with your local ssh client rather than the server. To close a stuck session, press these keys in order:

  1. Enter
  2. ~
  3. .

The ~ is only recognized at the start of a line, which is why you need to press Enter first. Press Enter, ~, ? to see the other available escape sequences.

Closing the terminal window works too. It won't damage anything on the server, but any command still running in that session will be terminated.

Keepalives: ServerAliveInterval and ServerAliveCountMax

Add the following two lines to your host entry in ~/.ssh/config on your local machine:

Host myserver HostName example.com User ubuntu IdentityFile ~/.ssh/id_ed25519_myserver ServerAliveInterval 60 # Sends a small keepalive message to the server every 60 seconds ServerAliveCountMax 3 # Gives up after 3 unanswered messages. If the connection is actually dead, you'll get your prompt back after about 3 minutes (60 ✕ 3) instead of being stuck with a frozen terminal. 

tmux: keep your work running when the connection drops

Keepalives make unexpected disconnections less likely, but Wi-Fi can still drop and laptops still go to sleep. When an SSH connection closes, the shell on the server typically receives a hangup signal (SIGHUP), which can cause the command you're running to terminate. This matters if you're in the middle of apt upgrade or a long Docker build.

tmux (terminal multiplexer) solves this problem. It runs your shell inside a session that persists on the server independently of your SSH connection. Your SSH connection simply provides access to that session. Your SSH connection becomes a window into that session. If the connection drops, the session and everything running inside it can keep going, and you can reconnect later and pick up right where you left off.

The four commands you need

Run these commands on the server:

Task Command
Start a named session tmux new -s deploy
Detach and leave the session running Ctrl+b, release, then d
List active sessions tmux ls
Reattach to a session tmux attach -t deploy

A workflow I use:

ssh myserver tmux new -s deploy docker compose up -d --build # Docker builds can take a while, so press Ctrl+b, then d, and close the terminal window. ssh myserver tmux attach -t deploy # Pick up where I left off 

Which one do you need?

Both. They solve different problems.

Keepalives tmux
Configured on Your local machine (~/.ssh/config) The server
What it does Makes idle connections less likely to drop and detects dead connections Keeps commands running after an SSH disconnection
Without it You may end up with a frozen terminal Long-running tasks may be interrupted

Cheat sheet

  • Stuck SSH session: Enter, ~, .
  • In ~/.ssh/config: ServerAliveInterval 60 and ServerAliveCountMax 3
  • Before running a long task: tmux new -s <name>
  • Detach: Ctrl+b, then d
  • Reattach: tmux attach -t <name>