Surviving Logout

GNU Screen & Linux Job Control

Chapter 7 · Keeping Jobs Alive After Logout

Chapter 1 explained, briefly, why a job dies when your SSH connection drops: the hang-up signal, SIGHUP. Chapter 5 showed that job numbers belong to one shell and vanish with it. This chapter looks at that problem in detail, and at the tools other than screen that solve it — nohup, disown and setsid — plus one systemd setting that can undo all of them.

How SIGHUP Reaches Your Jobs

connection drops │ ▼ the SSH session's terminal hangs up │ ▼ the kernel sends SIGHUP to your shell (the terminal's controlling process) │ ▼ bash sends SIGHUP to every job in its job table (stopped jobs are also sent SIGCONT, so they wake up and receive it) │ ▼ most programs exit when they receive SIGHUP

Every protection in this chapter breaks one link in that chain: either the job ignores the signal, or the shell never sends it, or the job isn't attached to the terminal at all.

"It survived last time" isn't proof
Whether an ordinary background job outlives your session depends on how the session ends — typing exit, closing a window, or a dropped connection — and on shell settings such as bash's huponexit option. A job that happened to survive once may not the next time. If it matters, protect it deliberately.

nohup: Start a Job That Ignores SIGHUP

$ nohup ./import.sh & [1] 70311 nohup: ignoring input and appending output to 'nohup.out' # Better: choose where the output goes yourself $ nohup ./import.sh > import.log 2>&1 &

nohup runs a command with SIGHUP set to be ignored. Because the terminal may not exist later, it also stops the command reading from the terminal, and if its output would go to the terminal, sends it to a file called nohup.out instead. Note the &: nohup doesn't put a job in the background on its own.

nohup must be used at the start
nohup is a program that starts another program. It can't be applied to a job that's already running — for that, you need disown.

disown: Protect a Job That's Already Running

disown is a bash builtin that works on jobs you've already started. It's the tool to reach for when you forgot nohup.

CommandEffect
disown %1Remove job 1 from the shell's job table, so the shell will never send it SIGHUP. It no longer appears in jobs.
disown -h %1Keep job 1 in the job table, but mark it so the shell won't send it SIGHUP. You can still use fg on it until you log out.
disown -aApply to all jobs
disown -rApply only to running jobs
# You started something long in the foreground and need to leave $ ./import.sh ^Z [1]+ Stopped ./import.sh $ bg # resume it in the background first $ disown %1 # then detach it from the shell $ exit
Always bg before disown
If you disown a stopped job, it stays stopped, with no shell left to resume it. Run bg first. You can still wake a stopped process later with kill -CONT PID, if you know its PID (Chapter 6).
What disown doesn't fix
disown only stops the shell sending SIGHUP. The job is still attached to the old terminal: if it later tries to write to it after the terminal has gone, the write fails and many programs stop. Redirect output to a file when you start anything you might need to disown — or use screen.

setsid: Start Completely Detached

setsid runs a command in a brand-new session with no controlling terminal at all, so no terminal hang-up can ever reach it.

$ setsid ./import.sh > import.log 2>&1 < /dev/null &

Comparing the Options

ToolUse on a running job?Can you interact with it later?Best for
nohupNoNo — read its logFire-and-forget commands you plan ahead
disownYesNo, once you log outRescuing a job you forgot to protect
setsidNoNoFully detached background programs
screenNot directly (Chapter 8)Yes — reattach and use it normallyAnything you want to watch, or type into, later

Screen's advantage is the last column. With the other tools, the job survives but you can never get its terminal back; with screen, the job's terminal survives too, so an interactive program — an installer asking questions, a database shell, top — carries on exactly as you left it.

The Catch: systemd's KillUserProcesses

On systems managed by systemd, the login manager has a setting, KillUserProcesses= in /etc/systemd/logind.conf. When it's yes, everything that belongs to your login session is killed when you log out — including nohup and disowned jobs, and screen sessions.

Check your system
systemd's own default is yes, but many distributions set it to no. If your screen sessions vanish when you log out, this is the likely reason. Ways around it include asking an administrator to enable "lingering" for your user (loginctl enable-linger), or starting the session outside your login's scope, for example with systemd-run --user --scope screen -S name.
And for real services
None of these tools is a substitute for a proper service. Anything that should start at boot and restart if it crashes belongs in a systemd unit (see the systemd in Depth course).

Hands-On Exercises

Exercise 1

In an SSH session, start nohup sleep 600 & and sleep 700 &. Close the terminal window (don't type exit). Log in again and use pgrep -a sleep to see which survived. Explain the result.

📄 View solution
Exercise 2

Start sleep 800 in the foreground and protect it so it survives logout, without restarting it. Then explain the difference between using disown and disown -h here.

📄 View solution
Exercise 3

Find out whether your system kills user processes at logout. Where would you look, and what would you check first if screen sessions kept disappearing?

📄 View solution

Chapter 7 Quick Reference

  • A lost terminal sends SIGHUP to the shell, which passes it to its jobs; most programs then exit
  • nohup cmd & starts a job that ignores SIGHUP, with output in nohup.out unless redirected
  • disown %n removes a running job from the shell's table; disown -h keeps it but stops the shell sending SIGHUP; always bg first
  • setsid starts a command with no controlling terminal at all
  • Only screen keeps the job's terminal, so you can interact with it again later
  • systemd's KillUserProcesses=yes kills everything at logout, screen included; check logind.conf, and use lingering or systemd-run --user --scope