Sitemap

Django Gunicorn with Nginx in Docker

4 min readApr 9, 2023

--

Official docs of django and docker
Prerequisite: python and docker installed

Press enter or click to view image in full size

Project Structure:

A. Setup Python Virtual Environment
create virtualenv: virtualenv venv
start virtualenv: venv/Scripts/activate
install Django and Gunicorn in virtualenv: pip install django gunicorn

B. Django Project with Gunicorn
Step 1: Create Django Project, Inital Migration
Create Django: django-admin startproject myproject
Go to myproject folder: cd myproject
Initial Migration: python manage.py migrate

Step 2: Create requirements.txt
create requirements.txt: pip freeze> requirements.

Step 3: Create Dockerfile in myproject

# syntax=docker/dockerfile:1
FROM python:3.8

# set environment variables
ENV APP_HOME=/app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# set work directory
WORKDIR $APP_HOME

# update pip, install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt $APP_HOME
RUN pip install -r requirements.txt

# copy app folder
COPY . $APP_HOME

# run python command
RUN python manage.py makemigrations
RUN python manage.py migrate
RUN python manage.py collectstatic --noinput --clear

C. Nginx Docker setup
Step 1: Preparation

Create nginx folder in main folder

Step 2: Create nginx.conf file

client_max_body_size 8M;

upstream django_app {
server myproject:8000;
}

server {

listen 80;

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
client_max_body_size 50M;

location / {
proxy_pass http://django_app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}

}

Step 3: Create Dockerfile in nginx folder

# Fetching the latest nginx image
FROM nginx

# Removing default nginx.conf
RUN rm /etc/nginx/conf.d/default.conf

# Copying new conf.d into conf.d nginx image
COPY nginx.conf /etc/nginx/conf.d

D. Docker Compose Setup

Create docker-compose.yml in main folder

version: '1.0'

# Defining the compose version
services:

nginx:
build: ./nginx
ports:
- 8687:80
depends_on:
- myproject
restart: "always"

myproject:
build: ./myproject
command: sh -c "gunicorn myproject.wsgi:application --bind 0.0.0.0:8000"
expose:
- 8000
restart: "always"
folder structure and its content

Docker-compose command:

docker-compose down && docker-compose build && docker-compose up -d

down for remove existing apps (if exist), build for building into image, up for start the services

Press enter or click to view image in full size
docker desktop

open in browser 127.0.0.1:8687

Press enter or click to view image in full size
django is served in nginx server

OTHER CONCERN: Debug=False, STATIC and MEDIA
Debug False is crucial for production env, STATIC and MEDIA should be served by nginx

Step 1: Django Myproject > Update myproject/myproject/settings.py

Set Debug, Allowed Host, Templates, Static and Media in settings.py

...

Debug = False #update
ALLOWED_HOSTS = ['127.0.0.1', 'localhost'] # update

...

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
Path(BASE_DIR, 'templates') # update
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]

...

STATIC_URL = '/static/' #new
STATIC_ROOT = Path(BASE_DIR, 'staticfiles') #new
STATICFILES_DIRS = [Path(BASE_DIR,'static'),] #new

MEDIA_URL = "/media/" #new
MEDIA_ROOT = Path(BASE_DIR, 'mediafiles') #new

...

Step 2: Django Myproject > Set static, media and templates folder

Create static, media and templates folder
Add image in static file, example: cat.png

Press enter or click to view image in full size
cat.png

Create index.html in templates folder

{%load static%}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello World</title>
</head>
<body>
<h1>Hello World!</h1>
<img src="{%static 'cat.png' %}" alt="not found">
</body>
</html>

Step 3: Django Myproject> Update myproject/urls for serving static file simulation

from django.urls import path
from django.shortcuts import render

def home(request):
return render(request, template_name='index.html')

urlpatterns = [
path('', home ),
]

Step 4: Nginx > Update nginx.conf Add staticfiles and mediafiles

client_max_body_size 8M;

upstream django_app {
server myproject:8000;
}

server {

listen 80;

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
client_max_body_size 50M;

location / {
proxy_pass http://django_app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}

# updated
location /static/ {
alias /app/staticfiles/;
}

# updated
location /media/ {
alias /app/mediafiles/;
}


}

Step 5: Docker Compose> Update docker-compose.yml add volumes

version: '1.0'

# Defining the compose version
services:

nginx:
build: ./nginx
ports:
- 8687:80
volumes:
- static_volume:/app/staticfiles
- media_volume:/app/mediafiles
depends_on:
- myproject
restart: "always"

myproject:
build: ./myproject
command: sh -c "gunicorn myproject.wsgi:application --bind 0.0.0.0:8000"
volumes:
- static_volume:/app/staticfiles
- media_volume:/app/mediafiles
expose:
- 8000
restart: "always"

volumes:
static_volume:
media_volume:

Final Folder Structure:

final project structure

Run with Docker-compose command:

docker-compose down && docker-compose build --no-cache && docker-compose up -d

open in browser 127.0.0.1:8687

Press enter or click to view image in full size

--

--