Minimal Gunicorn configuration

Gunicorn is an http server written in Python. It is a WSGI compliant server and can serve web applications which are compliant with WSGI. This would help if you aren’t comfortable with WSGI.

We will serve a basic web application using Gunicorn. And see the minimal and must configuration settings you should set while using gunicorn. We will specify the configuration variables in an external file and will direct gunicorn to use that configuration file.

Web application

We are working in directory /tmp and have activated a virtual environment called PythonEnv

(PythonEnv)/tmp $

Let’s write the web application which we will serve using gunicorn.

(PythonEnv)/tmp $ vim web_application.py

#web_application.py
def application(environ, start_response):
    path = environ.get('PATH_INFO')
    if path == '/':
        response_body = "Index"
    else:
        response_body = "Hello"
    status = "200 OK"
    response_headers = [("Content-Length", str(len(response_body)))]
    start_response(status, response_headers)
    return [response_body]

Serve application with gunicorn

Make sure to install gunicorn.

(PythonEnv)/tmp $ pip install gunicorn

Serve this application.

(PythonEnv)/tmp $ gunicorn -b localhost:8001 web_application:application

Make sure you can access http://localhost:8001/

With this, gunicorn is serving the application on localhost port 8001. We had to use -b flag to instruct gunicorn to bind on a particular port and host. There are tons of other options that can be set. Setting all of them on command line is a tedious task. So, we should have a configuration file.

Adding a configuration file

Let’s create a configuration file.

(PythonEnv)/tmp $ vim gunicorn_cfg.py

Bind on host and port

And add the following content in file gunicorn_cfg.py

bind='localhost:8001'

Now run gunicorn using this configuration file. Make sure to Ctrl+c the running gunicorn before proceeding with this.

(PythonEnv)/tmp $ gunicorn -c gunicorn_cfg.py web_application:application

Still gunicorn is running in the foreground, which we usually would not like on a production server. So, make it daemonized. Make sure to Ctrl+c the existing gunicorn process

But at some point we would like to kill the existing gunicorn process and restart gunicorn. Since it would be daemonized, we wouldn’t be able to kill with Ctrl+c. We would require the process id of running gunicorn process for killing it. So let’s keep track of process id of gunicorn before we make it daemonized.

Keep track of process id

Add following to gunicorn_cfg.py

pidfile='gunicorn_pid'

Run gunicorn

(PythonEnv)/tmp $ gunicorn -c gunicorn_cfg.py web_application:application

Open another terminal and ls. You’ll see that a file callled gunicorn_pid is created. cat gunicorn_pid and you will see a number. This number is the process id of gunicorn. Kill gunicorn with Ctrl+c again.

Make it daemonized

Add the following to gunicorn_cfg.py

daemon = True

Run gunicorn

(PythonEnv)/tmp $ gunicorn -c gunicorn_cfg.py web_application:application

Gunicorn is running as a daemon now. Make sure you can access http://localhost:8001/

Check process id of gunicorn

(PythonEnv)/tmp $ cat gunicorn_pid
19558

Kill existing gunicorn process and restart gunicorn.

(PythonEnv)/tmp $ kill 19558
(PythonEnv)/tmp $ gunicorn -c gunicorn_cfg.py web_application:application

This would establish that gunicorn_pid is working properly.

Add access loggging

At some point of time, your site will start behaving strangely and you would like to verify that requests are infact coming to gunicorn. So, there should be a file to log the requests.

Edit gunicorn_cfg.py and add:

accesslog='gunicorn_access.log'

Restart gunicorn for this change to take effect. You’ll have to read the gunicorn_pid file to read the process id of gunicorn for killing it, as we did earlier. Once you restart gunicorn, you will find a file named gunicorn_access.log created in the same directory as gunicorn_config.py

As you make requests to http://localhost:8001/, you will see more lines being logged to file gunicorn_access.log. Read about Linux tail command to know how to monitor a log file.

Add error logging

Let’s insert an error in our code. Add a = a + 1 as first line of function application in file web_application.py. ‘a’ is an undefined variable being used.

Restart gunicorn and try to access localhost:8001/

You get ‘Internal Server Error’ and you don’t have a way of knowing which line caused the error.

Add following line to gunicorn_cfg.py

errorlog='gunicorn_error.log'

Restart gunicorn for this change to take effect. You’ll find a file named gunicorn_error.log is added at the same level as gunicorn_cfg.py

We’ll again try to access localhost:8001/. But before that, let’s monitor the errorlog file using tail.

On terminal.

(PythonEnv)/tmp $ tail -f gunicorn_error.log

Now access http://localhost:8001/. Look at the terminal where tail is running. You’ll notice error logged on the terminal.

File "/tmp/web_application.py", line 3, in application
    a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment

So with an errorlog file, you can easily find out the cause of error. Undo the error we injected in code and restart gunicorn. Your application should start behaving properly again.

Read Gunicorn config file docs to know about more config options.

Thank you for reading the Agiliq blog. This article was written by Akshar on Jun 5, 2014 in gunicorn .

You can subscribe ⚛ to our blog.

We love building amazing apps for web and mobile for our clients. If you are looking for development help, contact us today ✉.

Would you like to download 10+ free Django and Python books? Get them here