Skip to main content

        GNU Screen: sessions that survive an SSH drop - Featured image

GNU Screen: sessions that survive an SSH drop

A long apt upgrade, a migration, a build — then the laptop sleeps. SSH dies, the process gets SIGHUP and dies with it. GNU Screen keeps the terminal on the server: disconnect, come back, the work is still there.

It is not a nohup replacement and not “another SSH”. It is a multiplexer: named sessions, several windows inside one, a shared session for two people. On older RHEL/Debian boxes screen is often already installed when tmux is not.

The minimal loop

On the server:

screen -S deploy

That is a normal shell. To leave without killing processes: Ctrl-a, release, then d. The session stays Detached.

List and resume:

screen -ls
screen -r deploy

If it is still Attached on another terminal (left it open at the office):

screen -d -r deploy

Detach there first, attach here.

Note

The prefix is Ctrl-a. Do not hold both keys together: Ctrl-a, release, then the letter. Ctrl-a a sends a real Ctrl-a into the program inside (needed by emacs, rarely by bash).

Install

# Debian / Ubuntu
sudo apt install screen

# RHEL / Alma / Rocky
sudo dnf install screen

# Alpine
sudo apk add screen

Check with screen -v. Vertical splits (Ctrl-a |) exist since GNU Screen 4.1; very old boxes will not have them.

Command-line flags

FlagWhat it doesExample
-S namecreate or address a session by namescreen -S logs
-ls / -listlist sessionsscreen -ls
-r [name]attach a detached sessionscreen -r logs
-d -r namedetach elsewhere, attach herescreen -d -r logs
-Rattach if it exists, otherwise createscreen -R logs
-x [name]attach without detaching the other sidescreen -x logs
-dmS name commandstart in the background, already Detachedscreen -dmS backup /opt/backup.sh
-Lwrite screenlog.N in the current directoryscreen -L -S migrate
-Logfile filelog path (with -L)screen -L -Logfile /tmp/migrate.log -S migrate
-X commandsend a command into a live sessionscreen -S logs -X stuff 'tail -f /var/log/nginx/error.log\n'
-wipedrop dead sockets from the listscreen -wipe

The -S name is what you look for in screen -ls. Without a name you get something like 12345.pts-0.hostname — awkward to recall.

Typical background job that must survive an SSH drop:

screen -dmS pg-dump pg_dump -Fc -f /backup/app.dump app
screen -ls
# later
screen -r pg-dump

When the command exits, the session usually disappears. Keep a shell afterwards:

screen -dmS build bash -lc 'make -j"$(nproc)"; exec bash'

Windows inside a session

One session, several windows: build, logs, a second shell. Switch without a new SSH connection.

KeyAction
Ctrl-a cnew window
Ctrl-a n / Ctrl-a pnext / previous
Ctrl-a 0Ctrl-a 9jump by number
Ctrl-a "window list, pick with arrows
Ctrl-a 'jump by number or name
Ctrl-a Arename the current window
Ctrl-a Ctrl-alast active window
Ctrl-a kclose the window (asks to confirm)
Ctrl-a \kill every window and quit screen

Window titles help once you have more than two: Ctrl-a Anginx-log.

Session, copy, split

KeyAction
Ctrl-a ddetach; processes keep running
Ctrl-a D Dpower detach (other displays too)
Ctrl-a ?key help
Ctrl-a :screen command line (quit, sessionname, …)
Ctrl-a asend Ctrl-a into the window
Ctrl-a [copy mode / scrollback
Ctrl-a ]paste screen’s paste buffer
Ctrl-a Escsame as Ctrl-a [
Ctrl-a Ssplit horizontally (region above/below)
Ctrl-a |split vertically
Ctrl-a Tabfocus the other region
Ctrl-a Xclose this region (window stays)
Ctrl-a Qkeep only this region
Ctrl-a Htoggle screenlog.N
Ctrl-a Mmonitor the window for activity (bell)
Ctrl-a xlock the session (user password)

Scrollback: Ctrl-a [, then arrows or PageUp / PageDown. Select: Space to start, arrows, Enter to copy into screen’s buffer. Leave the mode with Esc. Paste with Ctrl-a ]. This is not the system clipboard: the buffer lives inside screen.

After a split the new region is empty until you focus it (Ctrl-a Tab) and pick a window (Ctrl-a n or Ctrl-a ").

Server-side workflows

Long deploy. Named session, log on disk, close the lid:

screen -L -Logfile ~/migrate.log -S migrate
# inside: ansible-playbook -i prod site.yml
# Ctrl-a d

In the morning: screen -r migrate, or just tail -f ~/migrate.log.

Several jobs in one SSH. Session ops, windows build, journal, sql:

screen -S ops
# Ctrl-a c  — another window
# Ctrl-a A  — name it

Paired view. Same user, both attached:

# first
screen -S incident
# second, without kicking the first off
screen -x incident

Both see the same terminal. Faster than “paste me the output” during an incident.

Push a command into a session already running — no interactive attach:

screen -S ops -X screen bash
screen -S ops -X stuff 'systemctl status nginx\n'

-X screen opens a window; stuff types into it. \n is Enter.

A short .screenrc

Default scrollback is tiny, the startup banner gets in the way, and window names are easy to miss. In ~/.screenrc:

startup_message off
vbell off
defscrollback 20000
shell -$SHELL

hardstatus alwayslastline
hardstatus string "%{= kw}%-w%{= BW}%n %t%{= kw}%+w %= %H %l %Y-%m-%d %c"

defscrollback is how many lines Ctrl-a [ can walk. hardstatus is the bar at the bottom: windows, host, load, time. The file is read when a session is created; live sessions pick it up only after you recreate them.

You do not have to touch /etc/screenrc: the user file extends it.

Common failures

SymptomWhyWhat to do
There is no screen to be resumedwrong name, or the session already diedscreen -ls, then the exact name
Attached and -r refusessession still on another ptyscreen -d -r name
Session vanished after a commandthe only window’s process exitedwrap with bash -lc '…; exec bash'
Ctrl-a “eats” emacs/tmux insidescreen’s prefix winsCtrl-a a for a literal; or another escape in .screenrc: escape ^Bb
No vertical splitScreen < 4.1horizontal Ctrl-a S, or a newer package
No log fileno -L and nobody pressed Ctrl-a Henable it explicitly, check the session cwd

Sockets live in /run/screen/S-$USER/ or ~/.screen/. Another user’s session is not something you attach to by accident: the directory permissions block it.

When screen, when not

A single non-interactive background command is enough for systemd-run --user, tmux, or even nohup. A standing workspace on a bastion, a deploy you start and then close the lid on, shared log tailing — screen covers that without extra dependencies.

tmux is nicer for splits and config. Screen wins when it is already there on the box you just SSHed into. A named session and Ctrl-a d are the reason it stays in muscle memory.