Start a Command in a Separate Linux Session with setsid
A daemon you launched dies the moment you close the terminal that started it, because it inherited your shell's session. setsid is the fix: it runs a program in a brand new session, detached from the caller's session and process group. It is handy for small launch scripts, test processes and terminal-sensitive commands. You will finish knowing when setsid forks, how to capture the launched command's exit status, and when the terminal option is unsafe or unnecessary.
The route
Jump straight to the step you need, or tick off Done means at the end.
- Time: about 10 minutes.
- You need: a Linux shell and the
setsidcommand from util-linux. - Privileges: none. The examples do not alter services or configuration.
- Versions: the installed manual page on this system identifies the package as util-linux 2.39.3; the executable currently reports util-linux 2.42.4. Check the version on the machine where the command matters.
1. Confirm the command
Check that the executable is available and record its version:
command -v setsid
setsid --version
Expected output includes a path followed by a util-linux version line. The command accepts one program and then that program's arguments:
setsid program [argument ...]
Do not put a shell pipeline directly after setsid unless you deliberately want the shell to interpret it first. To run several shell operations as the new-session program, pass an explicit shell:
setsid sh -c 'printf "session work\n"; printf "finished\n"'
The two lines should be printed by the shell started through setsid. Quote the shell script as one argument so the calling shell does not execute it before setsid ever sees it.
2. Understand the default fork rule
Linux sessions contain process groups, and a process-group leader cannot call setsid(2) successfully. By default, setsid checks this condition: if it is already a process-group leader, it forks and lets the child create the new session; otherwise it runs the requested program in the current process.
That detail affects process IDs and parent-child relationships, but not the command's purpose: the requested program still ends up in a new session. If your caller needs a new process every time, request it explicitly:
setsid --fork sh -c 'printf "child pid: %s\n" "$BASHPID"'
Use --fork when the extra process is part of your launch design. Otherwise, leave the default alone: it avoids an unnecessary fork and is usually less surprising in scripts.
3. Preserve the launched command's exit status
Without --wait, the parent does not wait for a program started down the fork path, so a failing child looks the same as a succeeding one to your script. If you need to know whether the launched command succeeded, add --wait: setsid's own exit status then follows the executed program's:
setsid --wait sh -c 'exit 7'
printf 'setsid status: %s\n' "$?"
Expected output is:
setsid status: 7
This is the safer shape for a wrapper that must fail when its child fails. Test the status immediately, since the next command overwrites $?.
4. Choose terminal handling carefully
setsid normally creates a session with no controlling terminal. --ctty sets the controlling terminal to the current one, a terminal-management operation, not a general way to make a background command interactive. Only reach for it when the launched program specifically needs the current terminal and you know which terminal the caller owns:
setsid --ctty sh -c 'tty'
The output should be a terminal device when run from an interactive terminal. In a service, pipeline or redirected shell there may be no usable terminal at all, and the command can fail or behave differently. Do not add --ctty just because you saw it in an example.
Safety boundary
This guide does not use sudo, stop services or detach a production process. Adding --ctty can change where a program reads input and writes output, so test it against a harmless command before applying it to a long-running or interactive one. To undo the examples, let the short-lived commands exit; nothing persistent changed.
Common traps and recovery
- The child seems to outlive the script. You probably omitted
--wait. Add it when the caller must observe completion and status. - A pipeline runs in the wrong shell. Wrap the complete pipeline in
sh -cand quote it:setsid sh -c 'first | second'. Check each placeholder command before swapping in a real one. - The command is not found. Install or repair the util-linux package through your distribution's normal process; this guide does not prescribe package-manager commands because they differ by distribution.
- Terminal input disappears. Remove
--ctty, restore the original launch command, and retry with output redirected to a known file if needed. No terminal ownership change persists once these example processes exit. - The status is not what you expected. Use
--waitand print$?on the next line. A status from the shell that launchedsetsidis not a substitute for the child's status when the child was never waited for.
Done means
setsid --versionreports the installed util-linux version.- You can launch a command in a new session with
setsid program [argument ...]. - You use
--forkonly when a separate child process is required. - You use
--waitwhen the wrapper must return the child's exit status. - You reserve
--cttyfor a deliberate, tested terminal requirement.