Django Tip: How to configure Gunicorn to auto-reload your code during development
I just finished fully automating my entire server stack for my Django app with Ansible and Vagrant (using VirtualBox). One of the reasons I did this is to make my development environment as close to production as possible to hopefully eliminate any surprises when deploying to production. It also allows me to setup a development environment very quickly as I won’t have to deal with manual installation and configuration of different packages. In a team environment, the benefit of doing this multiplies.
This is basically my process:
1. Type in ‘vagrant up’ to create or start the VirtualBox virtual machine.
2. I have my virtual machine configured via Vagrant to share my local code (which is located in my Dropbox folder) with the virtual machine.
3. I make a change to my code, then I open my web browser and enter my virtual machine’s IP which is statically set to 192.168.33.10. The browser shows my changes.
What I want Gunicorn to do is similar to what the Django runserver does: automatically reload the application server when the code changes.
There are different ways to approach this, such as using a package called watchdog to watch the file changes and then restart Gunicorn. But it turned out there’s an even simpler way to do this with Gunicorn by setting the max_requests setting to 1 (full list of settings). When calling Gunicorn simply add this option (note that it’s 2 dashes in the beginning):
–max-requests 1
What this will basically do is tell Gunicorn to restart the process for every request, which would reload your code. It won’t know whether your code changed or not, it will always reload it. For production, this is probably not a good idea, but during development it’s a nice simple trick and you won’t really see a difference in performance as you’d be the only user.
Here’s the shell script that I use to start my Gunicorn process (note that I use Nginx to communicate with Gunicorn via a socket file, also note that I have variables set here which Ansible replaces with the actual values):
#!/bin/bash NAME="{{ application_name }}" DJANGODIR={{ application_path }} SOCKFILE={{ virtualenv_path }}/run/gunicorn.sock USER={{ gunicorn_user }} GROUP={{ gunicorn_group }} NUM_WORKERS=3 # Set this to 0 for unlimited requests. During development, you might want # to set this to 1 to automatically restart the process on each request # (i.e. your code will be reloaded on every request). MAX_REQUESTS={{ gunicorn_max_requests }} echo "Starting $NAME as `whoami`" # Activate the virtual environment. cd $DJANGODIR source ../../bin/activate source ../../bin/postactivate # Create the run directory if it doesn't exist. RUNDIR=$(dirname $SOCKFILE) test -d $RUNDIR || mkdir -p $RUNDIR # Programs meant to be run under supervisor should not daemonize themselves # (do not use --daemon). exec python manage.py run_gunicorn \ --settings={{ django_settings_file }} \ --name $NAME \ --workers $NUM_WORKERS \ --max-requests $MAX_REQUESTS \ --user=$USER --group=$GROUP \ --log-level=debug \ --bind=unix:$SOCKFILE
Tags: howto, django, ansible, tech, software development