At a glance
cmd &frees your prompt but dies on a dropped connection.nohup,setsidanddisownsurvive a closed terminal; redirect the output.systemd-run --userneedsloginctl enable-lingerto outlive your last logout.- tmux and screen are the only ones you can reattach to.
Which method survives what
When you close a terminal or your SSH connection drops, an interactive bash gets SIGHUP and passes it on to its jobs. We tested the methods below on bash 5.2, coreutils 9.7 and util-linux 2.41 by sending SIGHUP to an interactive bash.
| Method | Survives a closed terminal or SSH drop | Where the output goes | Reattach later |
|---|---|---|---|
cmd & | No; a clean exit spares it while huponexit is off, the default | The terminal | Only fg from the same shell |
nohup cmd > log 2>&1 & | Yes | log, or nohup.out without a redirect | No |
cmd & then disown | Yes, if its output is redirected | The terminal unless you redirect it | No |
setsid cmd > log 2>&1 < /dev/null & | Yes | log | No |
systemd-run --user | Yes, with loginctl enable-linger | The systemd journal | No |
| tmux or screen | Yes, unless logind has KillUserProcesses=yes | The tmux or screen window | Yes |
None of these survive a reboot, and none restart the job if it crashes.
Background jobs with &, jobs, fg and bg
& at the end of a command runs it in the background of your current shell, while its output still goes to your terminal. If you started a command in the foreground and want your prompt back, press Ctrl-z to suspend it and run bg to resume it in the background.
./backup.sh > backup.log 2>&1 &
jobs -l
fg %1
# Ctrl-z suspends it again, bg resumes it in the background
bg %1
kill %1jobs -l lists the current shell's jobs with their process IDs, and %1 means job 1. A job suspended with Ctrl-z shows as Stopped, not running, and in our test a stopped job died on SIGHUP.
A plain cmd & does not survive a dropped connection or a closed terminal, because bash sends SIGHUP to its jobs. On a clean exit it survives, because bash's huponexit option is off by default; with shopt -s huponexit in a login shell, such as an SSH session, it dies on exit too.
Keep it running after logout: nohup, disown and setsid
If you have not started the command yet, nohup cmd > log 2>&1 & makes it ignore SIGHUP. setsid cmd > log 2>&1 < /dev/null & starts it in a new session without a controlling terminal, so the hangup never reaches it. For a Python script, add -u or PYTHONUNBUFFERED=1, or the log fills in blocks.
nohup ./backup.sh > backup.log 2>&1 &
setsid ./backup.sh > backup.log 2>&1 < /dev/null &
# Already running as job 1? Keep it alive after logout
disown %1If the job is already running under cmd &, or you paused it with Ctrl-z and resumed it with bg, disown keeps it alive after you log out: bash removes it from the job table and never sends it SIGHUP. disown -h %1 keeps it in the table but marks it so SIGHUP is not sent. Both survived our test.
disown does not redirect output. In our test, a disowned Python job that kept printing to the terminal died within 2.5 seconds of the terminal closing, from the write error, while the same job with its output redirected kept running. Every detail of nohup's output files is in the nohup guide.
Run it as a systemd user service
systemd-run --user hands the command to your systemd user manager as a transient service. The user manager stops after your last session ends unless lingering is on, so run loginctl enable-linger first if you are logging out.
loginctl enable-linger
systemd-run --user --unit=train --same-dir python3 -u train.py
systemctl --user status train
journalctl --user -u train -f
systemctl --user stop trainsystemctl --user status train shows its state and recent output, journalctl --user -u train -f follows the output live, and systemctl --user stop train stops it. The service does not inherit your shell's environment, so pass variables with --setenv=NAME=value.
Or keep a terminal you can come back to
tmux and screen keep the job attached to a terminal you can reattach to later with tmux attach -t NAME or screen -r NAME. If systemd-logind kills user processes at logout (KillUserProcesses=yes, the upstream default that many distributions turn off), start them with systemd-run --scope --user and enable lingering. The tmux detach and attach guide has the full workflow.
Know when it finishes without logging back in
Every method above still makes you log back in to find out whether the job finished.
FAQ
What is the difference between nohup and disown?
nohup goes in front of the command when you start it, makes it ignore SIGHUP and sends its output to nohup.out if you do not redirect it. disown is a bash builtin you run after cmd &: it removes the job from the job table, or marks it with -h, so bash never sends it SIGHUP, but it does not redirect output.
How do I bring a background process back to the foreground?
In the shell that started it, run jobs -l and then fg %1 for job 1. After you log out, that shell and its job table are gone, so only tmux (tmux attach -t NAME) and screen (screen -r NAME) let you reattach.
How do I find my background process after logging back in?
jobs only lists jobs of the current shell, so it is empty in a new SSH session. Use pgrep -af name to find the process, or systemctl --user status train if you started it with systemd-run --user --unit=train.
Why did my background job die when I closed the terminal?
When the terminal closes or SSH drops, bash receives SIGHUP and passes it on to its jobs, which kills a plain cmd & job and a job stopped with Ctrl-z. A disowned job that still writes to the closed terminal can also die on the write error, so redirect its output.
How do I run a command in the background and save its output?
Redirect both streams before the trailing &, as in nohup cmd > log 2>&1 & or setsid cmd > log 2>&1 < /dev/null &. For a Python script, add -u or set PYTHONUNBUFFERED=1 so lines reach the log as they are printed.
Check on long runs from your phone
Change one import and every tqdm bar prints a link you can open on any device. Get a Discord or Slack message when the run finishes, fails or stalls.
pip install tqdm-liveSources
- Bash manual: Job Control Builtins gnu.org
- Bash manual: Signals and huponexit gnu.org
- GNU coreutils manual: nohup invocation gnu.org
- setsid(1) manual page man7.org
- systemd-run manual freedesktop.org
- loginctl manual: enable-linger freedesktop.org