Configuring nginx - Dockerizing Django for deploying anywhere !
Tags: docker, django, nginx
Published: 2021-01-18 15:12:28

We want to set up our web server in this part of the tutorial. So, a key issue while deploying django is **serving static files**. I googled a bit and there are quite a few interesting ideas floating around the internet :/ . So, it can get confusing. So, we want to resolve this RIGHT NOW. ! 

So in order to setup nginx, we create a new folder called 'proxy' at the same level as our src folder. 
```
├── proxy
│   ├── default.conf
│   ├── Dockerfile
│   └── uwsgi_params
├── scripts
│   └── entrypoint.sh
└── src
    ├── myapp1
    │   ├── admin.py
    │   ├── apps.py
    │   ├── __init__.py
    │   ├── migrations
    │   │   ├── __init__.py
    │   ├── models.py
    │   ├── static
    │   │   └── myapp1
    │   │       ├── css
    │   │       │   └── test.css
    │   │       ├── img
    │   │       │   ├── test.jpg
    │   │       │   └── test2.jpg
    │   │       └── js
    │   │           ├── test.js
    │   │           └── test2.js
    │   ├── templates
    │   │   └── admin_extend
    │   │       └── index.html
    │   ├── templatetags
    │   │   ├── custom_tags.py
    │   │   ├── __init__.py
    │   ├── tests.py
    │   ├── urls.py
    │   └── views.py
    ├── mysite
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── myapp2
    │   ├── admin.py
    │   ├── apps.py
    │   ├── __init__.py
    │   ├── migrations
    │   │   ├── 0001_initial.py
    │   │   ├── 0002_auto_20200918_1702.py
    │   │   ├── __init__.py
    │   ├── models.py
    │   ├── serializers.py
    │   ├── tests.py
    │   ├── urls.py
    │   └── views.py
    ├── db.sqlite3
    ├── manage.py

```
We create 3 empty files inside the proxy folder.
- default.conf (we setup our nginx configuration here)
- uwsgi_params (you need this for uwsgi with nginx :3 its in the official uwsgi documentation)
- Dockerfile (docker file for our nginx container)

**default.conf:**
```conf
server {
    listen 80;
    
    location /static {
        alias /vol/static;
    }

    location / {
        uwsgi_pass app:8000;
        include /etc/nginx/uwsgi_params;
    }
}
```
First, we let nginx listen to port 80 for deployment.
so, what we do is we serve any path with /static directly from nginx and we serve anything else from our uwsgi server which is running our app. Simple !

**usgi_params**
basically all you need to here is copy & paste this block in to your usgi_params:
```
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_ADDR $server_addr;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
```
its all in the documentation [here](https://uwsgi-docs.readthedocs.io/en/latest/Nginx.html#what-is-the-uwsgi-params-file)

**Dockerfile**
```docker
FROM nginxinc/nginx-unprivileged:1-alpine

COPY ./default.conf /etc/nginx/conf.d/default.conf
COPY uwsgi_params /etc/nginx/uwsgi_params

USER root

# setup static dirs
RUN mkdir -p /vol/static
RUN chmod 755 /vol/static

USER nginx
```
We use a unprivileged nginx container. This is for the same reason we mentioned earlier. We dont want to be running our docker containers with root user access. Notice, we switched to user nginx just after setting up the static dirs. This will ensure nginx will be run with the user 'nginx'

Now, we need to do a couple of things in our django **settings.py** before nginx can start serving our static files:
```python
STATIC_URL = '/static/static/'
STATIC_ROOT = '/vol/web/static'

MEDIA_URL = '/static/media/' 
MEDIA_ROOT = '/vol/web/media'  
```
Why? when we run our **collectstatic** command in our **entrypoint.sh** all the static files will be moved in to the STATIC_ROOT. Then, we would just map this directory to our proxy to serve the static files directly from nginx !!  
Now, the last thing we need is to handle our new dockerfile in our docker-compose. So, we need to add a new service for our proxy.
```yml
version: '3.7'

services:
  app:
    build:
      context: .
    ports:
      - "8000:8000"
    volumes:
      - production_static_data:/vol/web
    restart: always
    env_file:
      - .live.env

  proxy:
    build:
      context: ./proxy
    volumes:
      - production_static_data:/vol/static
    restart: always
    ports:
      - "80:80"
    depends_on:
      - app

volumes:
  production_static_data:
```
Notice, how we share our existing production_static_data docker volume between the two services. The easiest way to truly understand how this works is directly connecting to our running docker containers and see whats happening inside. Clear-as-a-day !
Run:
```
docker-compose up --build
```
this will build our two services 'app' & 'proxy'. Notice, we specified the 'depends_on' key in our docker-compose means the service 'proxy' depends on service 'app' and therrefore, app will be up and running first.
Now, lets find the **CONTAINER_ID** for the our docker containers using:
```
docker ps
```
now to connect to our docker containers:
```
docker exec -it <CONTAINER_ID> sh
```
this will give you shell access inside your docker container. Now, after connecting to either the 'app' or 'proxy' service, you should be able to find a folder called 'vol'. Go deeper and you should be able to find your static files in both the 'app' and 'proxy' containers. Simple !

And thats it for our nginx setup !! On the next part of this tutorial, we will start setting up a container for our database.

If you found this post helpful, CLICK BELOW 👇 <a href="https://www.buymeacoffee.com/ashiqurrahman" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Beer" style="height: 80px !important;width: 240px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>