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 = ['my_ip','myuser.mydomain.com'] #change: my_ip & 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': ['my_templates'], #change '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', #change 'USER': 'myuseruser', #change 'PASSWORD': 'my_db_password', #change 'HOST': 'my_ip', # change '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.