I wrote a shell.nix file to build the development environment for one of my projects. I'm using a shellHook to ensure a postgresql server is started when you drop into the nix-shell.

The shellHook is essentially:

export PGDATA=$PWD/nix/pgdata

pg_ctl start --silent --log $PWD/log/pg.log

Despite the fact that pg_ctl starts a server in the background, if I type Ctrl-C in the shell, the server shuts down. If I set up the same scenario outside of nix-shell, this does not happen.

I'm new to strace, but it looks to me like the postgresql process is receiving SIGINT when I type Ctrl-C in my terminal:

$ strace -p $postgres_pid
strace: Process 20546 attached
select(6, [3 4 5], NULL, NULL, {tv_sec=51, tv_usec=289149}) = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
write(2, "LOG:  received fast shutdown req"..., 37) = 37
kill(20550, SIGTERM)                    = 0

The postgresql process is attached to the same controlling terminal (pts/12) as my nix-shell process (though this is also true when I run it outside of nix-shell):

$ ps -p ${postgres_pid},${nixshell_pid} -o pid,ppid,wchan,tty,cmd
14608 18292 core_s pts/12   bash --rcfile /tmp/nix-shell-14608-0/rc
16355     1 core_s pts/12   /nix/store/xxxxxx-postgresql-9.6.8/bin/postgres

What's a good next step in debugging this? Should I read up on process groups?

Update: Trying a tip from another question, I found that this fixes the problem:

set -m
pg_ctl start --silent --log $PWD/log/pg.log

The weird thing is, according to $-, the m option was already set. Running echo $- produces imBH both before and after the set -m.

I noticed that in my interactive shells (whether nix-shell or not), $- is imBHs. The s is not present in the shellHook context, and I can't find an explanation of its meaning in the docs for Bash's set builtin. This may not be related though...


The manual says

start mode launches a new server. ... On Unix-like systems, by default, the server's standard output and standard error are sent to pg_ctl's standard output (not standard error). The standard output of pg_ctl should then be redirected to a file or piped to another process such as a log rotating program like rotatelogs; otherwise postgres will write its output to the controlling terminal (from the background) and will not leave the shell's process group. ...Use of either -l or output redirection is recommended.

It seems you need to redirect the output to detatch pg_ctl from the shell.

  • I'm using the -l option (--log), which supposedly will work as well. I tried using redirection instead (pg_ctl start --silent > $PWD/log/pg.log), but I get the same results. I also tried redirecting stderr as well (2>&1), still no difference. – ivan Aug 7 '18 at 11:55
  • I would consider that a bug in pg_ctl. You may try nohup, – RalfFriedl Aug 7 '18 at 16:58

It seems the problem was that the postgresql server was running as part of the same process group as the shell that launched it via pg_ctl. Typing propagated a SIGINT to all processes in the group.

One way to fix this is to launch postgresql in its own session using setsid.

setsid pg_ctl start --silent --log $PWD/log/pg.log

That said, I still don't know why this only happens in the context of shellHook.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.