Create Django deployment env in CentOS7 from scratch

Here we will create a cloud based virtual server from scratch, following things need to be done to prepare develop and deployment env for django1.7+.


Personally I prefer to setup deployment env before developing any project, put difficulties ahead may be a good thing. As python2.6 is not officially supported and django1.6 also end of the support life when I wrote this document, I choose to use python2.7 + latest django1.8. As CentOS6 only has python2.6 shipped, other application like yum depend on python2.6 to work properly, of course, we can install python2.7 manually but it may broke other existing system applications, so I choose CentOS7(more specifically CentOS7.1) as the deployment OS, which has python2.7 shipped by default, anyway still something need you pay attention to:

  • mysql-server package is not shipped by default in CentOS7
  • service start method is totally different with CentOS6

WSGI Server Choosing

Though there are many wsgi servers could be deployed for Django, and 3 combination way has been introduced in Django official site, I just choose the most popular one as our targets, the first combination is easy to deploy, and the second one could obtain much high performance and consume much less system resources(e,g: memory).

  • PlanA: Apache + mod_wsgi
  • PlanB: uWSGI + nginx


  • Create normal user
# useradd test
# passwd test
  • Make normal user to suders
# visudo
uncomment below line
%wheel  ALL=(ALL)       ALL
  • Add normal user to wheel group
# usermod -a -G wheel [username]
  • security harden on sshd service, change below on /etc/ssh/sshd_config
 port <specific_port>
 PermitRootLogin no
 MaxAuthTries 3
 X11Forwarding no

Installing Packages

  • upgrade system to the latest version
$ sudo yum update
  • python-pip
$ sudo yum install python-pip
$ sudo pip install --upgrade pip
  • mysql-server
$ sudo rpm -ivh
$ sudo yum install mysql-server
$ sudo systemctl enable mysqld
$ sudo systemctl start mysqld
  • mysql python library
$ sudo yum install MySQL-python
  • virtualenv related packages
$ sudo pip install virtualenv virtualenvwrapper
$ mkdir ~/.virtualenv
# Add following into ~/.bashrc file
export WORKON_HOME=$HOME/.virtualenvs
source /usr/bin/
  • Create test virtualenv for Django
$ source ~/.bashrc
$ mkvirtualenv test
$ lsvirtualenv
$ pip install "django==1.8.15"

PlanA: Apache + mod_wsgi

# sudo yum install httpd
# sudo yum install mod_wsgi


  • Assume you want deploy django web env for
  • Create a dir names “example” under “/var/www/html” directory
  • Create a static dir “static” under “/var/www/html/example/”, of course, it can be anywhere you like
  • Create a configuration file e,g: example.conf file under /etc/httpd/conf.d as following:
LoadModule wsgi_module modules/

WSGISocketPrefix /var/run/wsgi
<virtualhost *:80>
   DocumentRoot /var/www/html/example
   ErrorLog logs/
   CustomLog logs/ common
   Alias /static/ /var/www/html/example/static/
   <directory /var/www/html/example/static>
       Order deny,allow
       Allow from all

WSGIDaemonProcess example python-path=/var/www/html/example processes=1 threads=10 display-name=%{GROUP}
WSGIProcessGroup example
WSGIScriptAlias / /var/www/html/example/
<directory /var/www/html/example>
        Order deny,allow
        Allow from all

PlanB: Nginx + uWSGI

# sudo yum install nginx
# sudo yum install uwsgi uwsgi-plugin-python


  • Assume a virtualenv named ‘test’ already created, and django1.8 installed there
  • Start project named ‘proj’ under current user’s home directory, e,g: /home/ryan/
  1. Create a vassal common default config file
(test) [ryan@localhost ~]$  sudo vi /etc/uwsgi-vassals-default.ini
master = true
processes = 2
threads = 2
socket = /run/uwsgi/%(vassal_name).sock
# clear environment on exit, try to remove all of the generated file/sockets
vacuum = true
plugins = python
  1. Create a specific vassal config file under django project dir
(test) [ryan@localhost ~]$ startproject proj
(test) [ryan@localhost ~]$ cd ~/proj
(test) [ryan@localhost ~]$ vi proj-uwsgi.ini
vassal_name = proj
home_dir = /home/ryan

chdir = %(home_dir)/%(vassal_name)
home= %(home_dir)/.virtualenvs/test              <---- pls use the actual virtualenv dir you created, this is PYTHON_HOME 
module=%(vassal_name).wsgi:application           <---- pls use the real module path of file

3. Link the specific vassal config file in CentOS7.2 default /etc/uwsgi.d/ dir

 (test) [ryan@localhost ~]$ sudo ln -s proj-uwsgi.ini /etc/uwsgi.d/

4. Change user/group of proj-uwsgi.ini to uwsgi:nginx for “emperor-tyrant = true” is the default setting in /etc/uwsgi.ini

 (test) [ryan@localhost ~]$ sudo chown uwsgi:nginx proj-uwsgi.ini
When ’emperor-tyrant = true’ is set, the vassal process’s uid/gid will be set to the same as vassal config file, here it means file itself, not the uid/gid value in file content.

5. Change default /etc/uwsgi.ini to make it looks like below

(test) [ryan@localhost ~]$  sudo vi /etc/uwsgi.ini
uid = uwsgi
gid = uwsgi
pidfile2 = /run/uwsgi/
emperor = /etc/uwsgi.d
stats = /run/uwsgi/stats.sock
emperor-tyrant = true
cap = setgid,setuid
# include config templates to vassals config
vassals-include = /etc/uwsgi-vassals-default.ini
# clear environment on exit, try to remove all of the generated file/sockets
vacuum = true
  1. start uwsgi service and check it’s status
(test) [ryan@localhost ~]$ sudo systemctl start uwsgi
(test) [ryan@localhost ~]$ sudo systemctl status uwsgi -l
  • Configure nginx to establish connection with local unix socket created by uwsgi
(test) [ryan@localhost ~]$ vi /etc/nginx/conf.d/proj-nginx.conf
server {
    listen       80;
    location = favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        alias /home/ryan/proj/staticfiles/;
    location / {
       include uwsgi_params;
       uwsgi_pass unix:/var/run/uwsgi/proj.sock;
(test) [ryan@localhost ~]$ usermod -aG ryan nginx
(test) [ryan@localhost ~]$ namei -om /path/to/project

make sure all parent dir of /path/to/project all have execute permission

(test) [ryan@localhost ~]$ chmod 711 /home/ryan


  • Why we still get HTTP 502 error even follow above steps to configure nginx and uwsgi, detail error message recorded in nginx log file as below?

connect() to unix:/var/run/uwsgi/xxx.sock failed (13: Permission denied) while connecting to upstream, client:, server:, request: “GET / HTTP/1.1”, upstream: “uwsgi://unix:/var/run/uwsgi/xxx.sock:”, host: ““, referrer: “

The reason may be SELinux not having the policy for nginx to write to sockets. You may need completely disable selinux or add the enforcement policy for nginx



发送评论 编辑评论

 ̄﹃ ̄
∠( ᐛ 」∠)_
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
( ๑´•ω•) "(ㆆᴗㆆ)