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.
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
| Flag | What it does | Example |
|---|---|---|
-S name | create or address a session by name | screen -S logs |
-ls / -list | list sessions | screen -ls |
-r [name] | attach a detached session | screen -r logs |
-d -r name | detach elsewhere, attach here | screen -d -r logs |
-R | attach if it exists, otherwise create | screen -R logs |
-x [name] | attach without detaching the other side | screen -x logs |
-dmS name command | start in the background, already Detached | screen -dmS backup /opt/backup.sh |
-L | write screenlog.N in the current directory | screen -L -S migrate |
-Logfile file | log path (with -L) | screen -L -Logfile /tmp/migrate.log -S migrate |
-X command | send a command into a live session | screen -S logs -X stuff 'tail -f /var/log/nginx/error.log\n' |
-wipe | drop dead sockets from the list | screen -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.
| Key | Action |
|---|---|
Ctrl-a c | new window |
Ctrl-a n / Ctrl-a p | next / previous |
Ctrl-a 0 … Ctrl-a 9 | jump by number |
Ctrl-a " | window list, pick with arrows |
Ctrl-a ' | jump by number or name |
Ctrl-a A | rename the current window |
Ctrl-a Ctrl-a | last active window |
Ctrl-a k | close the window (asks to confirm) |
Ctrl-a \ | kill every window and quit screen |
Window titles help once you have more than two: Ctrl-a A → nginx-log.
Session, copy, split
| Key | Action |
|---|---|
Ctrl-a d | detach; processes keep running |
Ctrl-a D D | power detach (other displays too) |
Ctrl-a ? | key help |
Ctrl-a : | screen command line (quit, sessionname, …) |
Ctrl-a a | send Ctrl-a into the window |
Ctrl-a [ | copy mode / scrollback |
Ctrl-a ] | paste screen’s paste buffer |
Ctrl-a Esc | same as Ctrl-a [ |
Ctrl-a S | split horizontally (region above/below) |
Ctrl-a | | split vertically |
Ctrl-a Tab | focus the other region |
Ctrl-a X | close this region (window stays) |
Ctrl-a Q | keep only this region |
Ctrl-a H | toggle screenlog.N |
Ctrl-a M | monitor the window for activity (bell) |
Ctrl-a x | lock 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
| Symptom | Why | What to do |
|---|---|---|
There is no screen to be resumed | wrong name, or the session already died | screen -ls, then the exact name |
Attached and -r refuses | session still on another pty | screen -d -r name |
| Session vanished after a command | the only window’s process exited | wrap with bash -lc '…; exec bash' |
Ctrl-a “eats” emacs/tmux inside | screen’s prefix wins | Ctrl-a a for a literal; or another escape in .screenrc: escape ^Bb |
| No vertical split | Screen < 4.1 | horizontal Ctrl-a S, or a newer package |
| No log file | no -L and nobody pressed Ctrl-a H | enable 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.
