Prior to gunicorn and nginx, i’ve tried to user apache + mod_wsgi and failed. Still don’t know why it fail. After 3 days trying, I give up. I have to look for another method, I try using gunicorn dan nginx, and It took me only 2 days for gunicord + nginx setup to works. 🙂
Here is condition on my Django + Gunicorn + Nginx setup:
- I use CentOS 7 on my server
- I create new user for running django (in my case /home/myuser)
- I install python 3.7.3, django, pandas, django_extensions, virtual environment, gunicorn dan nginx
- My django project folder is /home/myuser/myproject
- I put all my django app (created using ‘python manage.py startapp myapp apps/myapp’) in /home/myuser/myproject/apps
Here is some of reference I use:
- http://docs.python-guide.org/en/latest/dev/virtualenvs/
- https://stackoverflow.com/questions/20126475/importerror-no-module-named-sqlite3-in-python3-3
- https://code.djangoproject.com/wiki/PythonPath
- https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu-16-04#prerequisites-and-goals
- https://docs.nginx.com/nginx/admin-guide/web-server/serving-static-content/
- https://musaamin.web.id/cara-menjalankan-django-dengan-gunicorn-dan-nginx-di-ubuntu-16-04/
- https://serverfault.com/questions/331256/why-do-i-need-nginx-and-something-like-gunicorn/331263#331263
- https://stackoverflow.com/questions/12800862/how-to-make-django-serve-static-files-with-gunicorn
- http://michal.karzynski.pl/blog/2013/06/09/django-nginx-gunicorn-virtualenv-supervisor/
- https://github.com/openai/gym/issues/757
- https://stackoverflow.com/questions/7475223/mysql-config-not-found-when-installing-mysqldb-python-interface
Here is my setup:
/home/myuser/myproject/wsgi.py
import os import sys from django.core.wsgi import get_wsgi_application os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings') sys.path.append('/home/myuser/myproject/apps') application = get_wsgi_application()
/etc/nginx/nginx.conf
user  nginx; worker_processes  1; error_log  /var/log/nginx/error.log warn; pid        /var/run/nginx.pid; events {    worker_connections  1024; } http {    include       /etc/nginx/mime.types;    default_type  application/octet-stream;    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '                      '$status $body_bytes_sent "$http_referer" '                      '"$http_user_agent" "$http_x_forwarded_for"';    access_log  /var/log/nginx/access.log  main;    sendfile        on;    #tcp_nopush     on;    keepalive_timeout  65;    #gzip  on;    include /etc/nginx/conf.d/*.conf; }
/etc/systemd/system/gunicorn.service
setup for gunicorn to be run as service.
[Unit] Description=gunicorn service After=network.target [Service] User=myuser Group=myuser WorkingDirectory=/home/myuser/myproject/ ExecStart=/home/myuser/app1/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/myuser/myproject/myproject.sock myproject.wsgi:application --log-level debug [Install] WantedBy=multi-user.target
don’t forget to enable service using:
systemctl enable gunicorn
/home/myuser/myproject/myproject/setting.py
import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SECRET_KEY = '6sp(_!o*NOT_MY_REAL_KEY_OF_COURSE*uu5o(9q1+ir=:-)r' # change: my key DEBUG = True # to be changed on production ALLOWED_HOSTS = ['192.168.0.2','myuser.mydomain.com'] #change: my domain # Application definition INSTALLED_APPS = [    'django.contrib.admin',    'django.contrib.auth',    'django.contrib.contenttypes',    'django.contrib.sessions',    'django.contrib.messages',    'django.contrib.staticfiles',    'django.contrib.humanize',    'django_extensions',    'myproject',    'myproject.templatetags', # delete:my custom tags    'login', # delete:my app    'weeklyreport', # delete:my app ] MIDDLEWARE = [    'django.middleware.security.SecurityMiddleware',    'django.contrib.sessions.middleware.SessionMiddleware',    'django.middleware.common.CommonMiddleware',    'django.middleware.csrf.CsrfViewMiddleware',    'django.contrib.auth.middleware.AuthenticationMiddleware',    'django.contrib.messages.middleware.MessageMiddleware',    'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'myproject.urls' TEMPLATES = [    {        'BACKEND': 'django.template.backends.django.DjangoTemplates',        'DIRS': ['templates'],        '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',            ],        },    }, ] WSGI_APPLICATION = 'myproject.wsgi.application' DATABASES = {    'default': { #change: my database setup        'ENGINE': 'django.db.backends.mysql',        'NAME': 'mydb1',        'USER': 'myuseruser',        'PASSWORD': 'my_db_password',        'HOST': '192.168.0.2',        'OPTIONS': {            'read_default_file': '/etc/my.cnf.d/client.cnf',            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'; SET SESSION binlog_format = 'ROW';",        },        'jgmotordb': { #delete: my database setup        'ENGINE': 'django.db.backends.mysql',        'NAME': 'mydb2',        'USER': 'myuseruser',        'PASSWORD': 'my_db_password',        'HOST': '192.168.0.2',        'OPTIONS': {            'read_default_file': '/etc/my.cnf.d/client.cnf',            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'; SET SESSION binlog_format = 'ROW';",            }        }    } } AUTH_PASSWORD_VALIDATORS = [    {        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',    },    {        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',    },    {        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',    },    {        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',    }, ] LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True STATIC_URL = '/static/' # my static folder STATICFILES_DIRS = [    os.path.join(BASE_DIR, '0static'), # my static folder (before for 'collectstatic' command) ] STATIC_ROOT = os.path.join(BASE_DIR, 'static') AUTHENTICATION_BACKENDS = ( 'myproject.backend.custombackend', ) #delete: my custom backends
It’s my django project. Therefor, you might find my setup is not ideal, but it works! 🙂 (and it took me a week to make it works).
I learn that, most of tutorials use apt / apt-get for installation. Since I use CenOS (which use yum as default) I find it annoying. 😦
Please leave a comment for any suggestion or if you find this post helpful.