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.
setsid command from util-linux.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.
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.
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 $?.
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.
--wait. Add it when the caller must observe completion and status.sh -c and quote it: setsid sh -c 'first | second'. Check each placeholder command before swapping in a real one.--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.--wait and print $? on the next line. A status from the shell that launched setsid is not a substitute for the child's status when the child was never waited for.setsid --version reports the installed util-linux version.setsid program [argument ...].--fork only when a separate child process is required.--wait when the wrapper must return the child's exit status.--ctty for a deliberate, tested terminal requirement.