If you're using Supervisor and adding Django to it via
[program:some_django] command=python manage.py runserver directory=/dir/to/app
then you may notice that when you stop the some_django program (or kill the process) there is still a process running from your runserver command. And if you try to start the server back up, you'll raise an error because the port is in use!
If you look close enough, you'll see that this process's parent ID is the one which is now dead. What we have here is a rogue orphan.
[program:some_django] ... stopsignal=KILL killasgroup=true
I tried and tested it by executing
supervisorctl stop some_django
but the orphaned process was still there. Looking into the docs, I found what I needed. You see, killasgroup kills all processes in the group when Supervisor resorts to sending a SIGKILL. The idea is that when the process is playing nicely, it should be allowed to handle stoping it's children on its own. But when it misbehaves and needs a bullet to the bits, you may need an option which that gives you the power to drag the whole family to the gallows for execution as well. From CHANGES.txt:
- Add a boolean program option `killasgroup`, defaulting to false, if true when resorting to send SIGKILL to stop/terminate the process send it to its whole process group instead to take care of possible children as well and not leave them behind. Patch by Samuele Pedroni.
But when I ran the stop command, and even through the stopsignal was set to KILL, Supervisor wasn't inside the blocks that consider the killasgroup setting.
After digging through the docs and source, I found that I needed to use stopasgroup (which was added after killasgroup). From CHANGES.txt again:
- Add a boolean program option `stopasgroup`, defaulting to false. When true, the flag causes supervisor to send the stop signal to the whole process group. This is useful for programs, such as Flask in debug mode, that do not propagate stop signals to their children, leaving them orphaned.
So, the settings that works for running Django's runserver command in Supervisor is:
[program:some_django] command=python manage.py runserver directory=/dir/to/app stopasgroup=true
A few closing notes:
Setting stopasgroup to true sets killasgroup to true as well. See options.py:
stopasgroup = boolean( get(section, 'stopasgroup', 'false')) killasgroup = boolean( get(section, 'killasgroup', stopasgroup))
Setting stopasgroup to true and killasgroup to false raises an error. From options.py again:
if stopasgroup and not killasgroup: raise ValueError( "Cannot set stopasgroup=true and killasgroup=false" )