1. Introduction

As Linux users, we often use the terminal to run various commands and programs. We run commands that take more than just a moment to complete in various scenarios. In such cases, we may want to run the command in the background so the terminal is free for other work. Or, we may be running the command remotely in an SSH session, in which case we can conveniently start the process in the background and exit the session after that.

In this tutorial, we’ll look at several ways to detach a process from the terminal entirely. We can use some methods to start the process in the background, while some methods help us move an already running process to the background.

2. Using bg, fg, and jobs

Once a command has started running, we can hit Ctrl+Z to freeze the process and then use the bg command to resume it in the background. We can then use the jobs command to view the running backgrounded processes:

$ sleep 10
^Z
[1]+  Stopped                 sleep 10
$ bg
[1]+ sleep 10 &
$ jobs
[1]+  Running                 sleep 10 &
$ echo

[1]+  Done                    sleep 10

The above snippet shows the output for a process stopped with Ctrl+Z and then moved to the background with bg. Running the jobs command tells us that the previous command is indeed still running but in the background. Running another random command (echo) after 10 seconds tells us that the initial command has been completed.

Once we move a process to the background using the bg command, it is possible to bring it back to the foreground using the fg command:

$ (sleep 11 && echo hello)
^Z
[1]+  Stopped                 ( sleep 11 && echo hello )
$ bg
[1]+ ( sleep 11 && echo hello ) &
$ fg
( sleep 11 && echo hello )
hello

In the above snippet, the initial command is supposed to wait for 11 seconds and print “hello”. After we send it to the background and bring it back to the foreground using fg, we see that the command prints “hello” in the foreground.

3. Using the & Operator

We can start a command in the background by appending “&” to the end** of the command:

$ gedit &
[1] 968967
$ 
(gedit:968967): GtkSourceView-WARNING **: 16:15:38.174: could not parse color '#bg'

(gedit:968967): GtkSourceView-WARNING **: 16:15:38.175: no color named 'white'
$ echo "running another command"
running another command
$ kill 968967

From the above, we see that appending an ampersand to the gedit command sends it to the background and prints its PID, 968967. Gedit is a GUI program, and it opens up on the desktop while leaving the terminal free to run other commands. When we run the kill command on its PID, the program exits.

We also see that the output from the gedit command is still printed on this terminal. To avoid that, we can redirect the output to another location, say /dev/null:

$ gedit 1>/dev/null 2>/dev/null &
[1] 979813
$ kill 979813
$ echo "hello"
hello
[1]+  Terminated              gedit > /dev/null 2> /dev/null

In this case, we redirected the output and error streams to /dev/null, and the output we saw earlier is now suppressed. As a result, we only get notified of the PID on our terminal initially and then only when the process has exited.

4. Using nohup

The nohup command is used to run a command in a way that is immune to “hangups” or terminal disconnections. When we start a command using nohup, the command redirects the output to nohup.out:

$ nohup echo hello &
[1] 998797
nohup: ignoring input and appending output to 'nohup.out'
$
^C
[1]+  Done                    nohup echo hello
$ cat nohup.out
hello

In the above snippet, we start a command using nohup, adding “&” at the end, so the terminal is free to run other commands. Once the process completes, we find the command’s output in the nohup.out file.

5. Using disown

We can run a command and have the terminal disown the process by appending “& disown** at the end:

$ echo hello & disown
[1] 1007802
hello

After disowning, we see that the command prints the PID first, followed by the output from the program – which still appears on our terminal. To silence the output, we can redirect it to /dev/null, as we did earlier:

$ echo hello 1>/dev/null 2>/dev/null & disown
[1] 1016729

Now, the command prints only the PID and not the command’s output.

6. Using setsid

When we run a command with setsid, the command starts in a new session that is disconnected from the current terminal. Similar to disown and using “*&*“, the output is printed on the current terminal, and we can silence it by sending it to /dev/null:

$ setsid echo hello
hello
$ setsid echo hello 1>/dev/null 2>/dev/null

In the latter case, the command prints no output on the terminal.

7. Using screen

Screen is a window manager that allows us to start and manage multiple virtual terminals. To run a process in the background using screen, we can create a new window, start the process there and detach the window.

7.1. Entering a Screen Window

To enter a new screen window, we simply type the screen command:

$ screen
[screen_window] $ 

Once we are in, we can press Ctrl+A followed by ” (double quotes) to list all active screen windows:

[screen_window] 
Ctrl+A " 
 Num Name 
   0 bash

We see that there is only one screen window. Please note that “[screen_window]” is shown in the code snippets only to differentiate it from the original terminal, and it is not actually printed in the output.

7.2. Start a Command in the Screen Window

Now let’s start a long-running command inside our screen window:

$ watch -n 1 date

This will show output similar to below that continuously refreshes itself every second until we exit using Ctrl+C:

Every 1.0s: date                          dell: Thu Jun  2 17:13:14 2022

Thursday 02 June 2022 05:13:14 PM IST

7.3. Detach From the Screen Window

Now, we can detach from this screen window and go back to our original terminal session by pressing Ctrl+A followed by d:

[screen_window}
Ctrl+A d
$ screen
[detached from 1045567.pts-1.dell] 

When we return to the original terminal, we see the screen command we typed earlier. We also see some new output below, which says we detached from a screen window and got back here.

7.4. Go Back to the Screen Window

We can re-enter our screen window by running screen with the -R flag:

$ screen -R
[screen_window]
Every 1.0s: date                     dell: Thu Jun  2 17:23:05 2022

Thursday 02 June 2022 05:23:05 PM IST

We see that the watch command we started earlier is still running inside this screen window.

7.5. Exit the Screen Window

To exit this screen window, we can press Ctrl+C to stop the process. Then, use the exit command to close the window:

[screen_window]
Ctrl+C
[screen_window] $ exit

$ screen -R
[screen is terminating]

After exiting the screen window, we go back to the original terminal. Here, we see the screen -R command we previously ran, along with output that says screen is terminating.

8. Using pm2

For cases when we need to detach a program from the terminal and keep it running forever, we can use a program called pm2. This is best suited for running application servers or bots that need to be online 24×7 and automatically restart if a crash occurs. Pm2 takes care of all that.

8.1. Installing pm2

Before installing pm2, we need to ensure that we have nodejs and npm installed by running the following commands:

$ node -v
v14.18.1
$ npm -v
6.14.15

Then we can install pm2:

$ sudo npm install -g pm2

8.2. Running Programs With pm2

We can start a nodejs program with pm2 using pm2 start:

$ pm2 start test.js
[PM2] Spawning PM2 daemon with pm2_home=/home/kd/.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting /home/kd/tinkering/test.js in fork_mode (1 instance)
[PM2] Done.
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0  │ test               │ fork     │ 0    │ online    │ 0%       │ 27.5mb   │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
[PM2][WARN] Current process list is not synchronized with saved list. App start_bot differs. Type 'pm2 save' to synchronize.

Once the program begins, we can view the current status by running pm2 status:

$ pm2 status
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0  │ test               │ fork     │ 18   │ online    │ 0%       │ 40.8mb   │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
[PM2][WARN] Current process list is not synchronized with saved list. App start_bot differs. Type 'pm2 save' to synchronize.

While pm2 was mainly built to run nodejs programs, it can handle others, such as python programs:

$ pm2 start test.py
[PM2] Starting /home/kd/tinkering/test.py in fork_mode (1 instance)
[PM2] Done.
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0  │ test               │ fork     │ 37   │ online    │ 100%     │ 37.3mb   │
│ 1  │ test               │ fork     │ 0    │ online    │ 0%       │ 4.0kb    │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
[PM2][WARN] Current process list is not synchronized with saved list. App start_bot differs. Type 'pm2 save' to synchronize.

We see that pm2 assigns an ID to every program started. We can use this id to view the output or stop the program:

$ pm2 logs 0
[TAILING] Tailing last 15 lines for [0] process (change the value with --lines option)
/home/kd/.pm2/logs/test-error.log last 15 lines:
/home/kd/.pm2/logs/test-out.log last 15 lines:
0|test     | hello
0|test     | hello
0|test     | hello
0|test     | hello
0|test     | hello
0|test     | hello
0|test     | hello
0|test     | hello
0|test     | hello
0|test     | hello
0|test     | hello
0|test     | hello
0|test     | hello
0|test     | hello
0|test     | hello
$  pm2 stop 0
[PM2] Applying action stopProcessId on app [0](ids: [ '0' ])
[PM2] [test](0) ✓
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0  │ test               │ fork     │ 94   │ stopped   │ 0%       │ 0b       │
│ 1  │ test               │ fork     │ 15   │ errored   │ 0%       │ 0b       │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
[PM2][WARN] Current process list is not synchronized with saved list. App start_bot differs. Type 'pm2 save' to synchronize.

We wrote both programs to print “hello” and exit, and pm2 tries to restart them every time they exit. So, we see a stream of lines saying “hello” in the output.

9. Conclusion

In this tutorial, we looked at several ways to run programs detached from the terminal.

We can use the & operator, and the nohupdisownsetsid, and screen commands to start a process detached from the terminal. However, to detach a process that has already started, we need to use the bg command after pausing the process using Ctrl+Z.

We also looked at the pm2 command which we can use for more elaborate scenarios such as running application servers. However, this does not come bundled with common Linux distributions by default. We need to install it along with other dependencies.