Deploy/Run a Django app with Apache and Gunicorn in Ubuntu

Here is apache configuration file when django is run by Gunicorn.


    Alias /static /home/ambu/myproject/static
        Require all granted

    ProxyPreserveHost On
    ProxyPass "/static/" !
    ProxyPass        "/" ""
    ProxyPassReverse "/" ""
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

Deploy/Run a Django app with Nginx, Gunicorn and Supervisor in Ubuntu

The reason we need Supervisord

Right now, we have our app running with Nginx and Gunicorn. However, every time our machine boots we have to start gunicorn and overall, controlling (stopping, restarting and etc) gunicorn is very difficult. What we want is an easy way of doing so.

Welcome Supervisord which allows us to monitor and control a number of processes on UNIX-like operating systems.

Let’s remember how we used to start our app:

gunicorn --daemon --workers 3 --bind unix:/home/ubuntu/myproject/myproject.sock myproject.wsgi

With supervisord at our disposal, it will be very easy and convenient to execute those commands:

supervisorctl start myproject
supervisorctl stop myproject
supervisorctl restart myproject

Installation and Setup

To install, type the following:

sudo apt-get install supervisor

Now, restart it:

sudo service supervisor restart

The main configuration file of supervisord is here /etc/supervisor/supervisord.conf. If we take a look, we will see that it contains these lines:

files = /etc/supervisor/conf.d/*.conf

It means that config files of specific projects can be stored here /etc/supervisor/conf.d/ and they will be included in that main file.

So, let’s create myproject.conf in /etc/supervisor/conf.d/ folder:

sudo vim /etc/supervisor/conf.d/myproject.conf

and configure our project:

command=/home/ubuntu/myprojenv/bin/gunicorn --workers 3 --bind myproject.wsgi

Let’s save the file and execute the following commands to bring these changes into effect:

sudo supervisorctl reread
sudo supervisorctl update

Well, that’s pretty much it. Simple, right? To verify that everything is working, type this:

ps ax | grep gunicorn

You should see several gunicorn processes running. Or, you can go to localhost:8000 and you will see your django app up and running.

Or, you can now use supervisor to check whether your app is running:

sudo supervisorctl status myproject


Deploy/Run a Django app with Nginx and Gunicorn in Ubuntu

The reason we need Nginx

If you followed the previous tutorial, we ran our django app with Gunicorn. However, at the end, we saw that the styles of the admin panel were gone. The reason is that Gunicorn is an application server and just runs the app (django app in our case) and django, as we know, does not serve static files except in development. Nginx to the rescue! It will be a reverse proxy for Gunicorn. What the hell is a reverse proxy? Good question! We all know what VPNs are, right? We use them to access some website that is blocked for some reason. In this case, we access that website through a VPN: We -> VPN -> some website. This kind of proxies are called Forward Proxies. As for reverse proxies, think of them as forced proxies. For example, a user is trying to access our django app running in gunicorn. He thinks that he is accessing the app directly. However, what is happening is that he is first accessing the Nginx server which decides what to do next. If the user is accessing a static file, the Nginx server will serve it itself. Otherwise, it will redirect it to Gunicorn. In plain terms, http requests will be handled by Gunicorn and static ones by Nginx. That’s why we need Nginx.

Apart from that, Nginx also improves performance, reliability, security and scale.


By now we already have Django and Gunicorn ready. So, let’s install Nginx now:

sudo apt-get install nginx

Now, we will configure Nginx to pass traffic to the process.

Create a file /etc/nginx/sites-available/domain and type in the following:

server {
    listen 8000;

    location = /favicon.ico { access_log off; log_not_found off; }

    location /static/ {
            root /home/ubuntu/myproject;

    location / {
            include proxy_params;

Now, let’s enable this file by linking it to the sites-enabled folder:

sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

Restart nginx:

sudo service nginx restart


Deploy/Run a Django app with Gunicorn

Here is my directory structure of Django app:

├── myproject
│   ├──
|   ├── 
├── app_folder
├── virtualenv

Install Guinicorn using pip:

pip install gunicorn

Now you might be wondering what is that wsgi thing is. Well, it stands for Web Server Gateway Interface and basically is a way how apps/frameworks and servers talk to each other. If the server(like Gunicorn) has wsgi implemented and so has your framework(Django), it means that you can run your app with that server. And the entry point of communication for these two is the variable application, which is located in myproject/ in our case.

Let’s play with it a little bit to see what it got.

We can bind it to a specific port:

gunicorn --bind myproject.wsgi

You can increase the number of workers to serve requests, which you probable will in real life as your users increase:

gunicorn --workers 3 myproject.wsgi

Run it in a daemon mode:

gunicorn --daemon myproject.wsgi

Or all of them altogether(a shorter version):

gunicorn --daemon -b -w 3 myproject.wsgi

After running your app with gunicorn, go to the django admin panel at localhost:8000/admin. You will see that all styles are gone. The reason is that gunicorn is an application server and it does not serve static files. In order to solve this problem, we will take a look at Nginx next and use it as a reverse proxy for gunicorn. We will talk about what reverse proxy is as well so don’t think about it for now.