Setup Django behind uWSGI and NGINX on CentOS 7

Setting up a web server for Django could be challenging and headache. Let’s try to make it simple: Django behind uWSGI and NGINX on CentOS 7 from scratch. At the end, our complete stack of components will look like this:

the web client <-> the web server <-> the socket <-> uwsgi <-> Django

1. Install Dependencies

Assuming that you are working on a smallest VPS like mine (1GB of RAM, 1 vCPU, 25GB SSD). I am currently use Linode and DigitalOcean.

1.1. NGINX

yum install epel-release -y
yum install nginx -y

1.2. Python 3 & PIP

yum install python34-devel gcc -y
curl -O

1.3. Create VirtualEnv with Python3

pip install virtualenv
mkdir -p /var/www && cd /var/www
python3.7 -m venv p3venv

If you are up-to-dated person, you can install 3.6.2 (latest python version as of now – Aug 28, 2017) follow this instruction

1.4. Install uWSGI & Django

# Activate virtual environment
source p3venv/bin/activate
pip install uwsgi
pip install django

2. Configurations

2.1. Basic NGINX config

For simplest & testing purposes, let’s create NGINX server block by issuing “vi /etc/nginx/conf.d/django.conf”. Any *.conf file inside this folder will be loaded as per instructed by main & default NGINX configuration (/etc/nginx/nginx.conf).

Save NGINX config and start NGINX service: systemctl start nginx

As of now, we have NGINX serves static files and by pass others to Django Server which will be configured shortly. It means you will get 502 bad gateway when accessing the site but this is totally fine.

2.2. Create Django project

# Make sure we are in right place
cd /var/www/example startproject djangodemo
# Also allow domain or IP in Django settings (/var/www/example/djangodemo/djangodemo/

Test if they look good by starting Django Development and uWSGI server. You will get “It worked! Congratulations on your first Django-powered page.”

python runserver ("ctrl + c" to terminate)
uwsgi --http :8000 --module djangodemo.wsgi ("ctrl + c" to terminate)

Alright, let’s configure uWSGI as service so we don’t have to keep terminal open.

2.3. Configure uWSGI as service

Save djangodemo_uwsgi.ini file and create symlink from the default config directory to your config file

ln -s /var/www/example/djangodemo/djangodemo_uwsgi.ini /etc/uwsgi/vassals/

Quick test if the configuration is good by start uWSGI server and navigate to the site. You should get “It worked! Congratulations on your first Django-powered page.”
/var/www/p3venv/bin/uwsgi --emperor /etc/uwsgi/vassals
Ctrl + C to terminate uWSGI server and let’s make it runs as a service

Start uWSGI and NGINX services and you should be able to access to your Django app without having to hold terminal open.

systemctl start uwsgi
systemctl restart nginx

Final thought

Congratulations. You’ve completed setting up NGINX, uWSGI to serve Django application. I know this is not so easy, especially when you are new to Django and uWSGI like me. It took me almost 2 weeks to search and try things out before writing this article.

I am still stuck at djangodemo_uwsgi.ini with chmod-socket = 666. Whenever I change it to chmod-socket = 664, I get 502 bad gateway. If someone knows the cause and how to fix it, please let me know.

Any input or comment are more than welcomed and appreciated. So why not leave a comment now, huh?

Image credit:


Demystifying 3 Common Misconceptions About Xpath In Web Automation


Element identification lies at the core of automating web tests because without it, your test automation tool has no clue about how to locate and interact with the correct web elements on your application under test. As recommended by W3CXPath is today’s solution of choice widely adopted by many web automation solutions including the famous Selenium framework. However, just like mastering a katana, making use of XPath up to the proficient level requires quite some time and deliberate effort. This article aims to help you take full advantage of XPath by unveiling the 3 common yet deadly misconceptions which novice testers might pick up while learning this powerful “secret weapon”.

Miconceptions Unveilved

[1] XPath is an inherent property of the web element

Unraveling XPath this way is natural when you are learning the ropes of XPath but later on, it reveals itself as a dangerous analogy. Let’s say you’re copying the XPath of a button (<input>) on Chrome by right clicking on the element and choosing Copy > Copy XPath:

Copy XPath


This is what you get:


Meanwhile, if you use Firebug (an add-on of Firefox) on the same particular button, you will receive an utterly different result:


How on earth the XPath “property” of the same element is not even remotely similar provided that the element has not changed a wee bit?

It’s because XPath is indeed not a control’s “property” after all. By definition, XPath is a language to tell a browser, a test tool or any other pieces of software how to navigate an XML document and find the specified element. Hence, it’s very normal that different tools return different XPaths for the same exact element.

This implicates that it’s up to you to determine which XPath is the most readable yet reliable to identify an element.

Some test automation frameworks such as LogiGear’s TestArchitect™ offer a built-in feature to purposefully construct an elegant XPath to efficiently locate your web element. For instance, the screenshot below shows TestArchitect’s suggested XPath for the aforementioned button.

Interface Viewer


This path (//input[@id=’Login’]) is more elegant because:

  • It’s more specific than Chrome’s XPath (//*[@id=”Login”]) which accepts all tags as long as it has the predefined ID. Chrome’s suggestion would return the wrong element in case there happens to be a <td> with the identical ID (very likely).
  • It’s less fragile than Firebug’s XPath (/html/body/div/table/tbody/tr[5]/td/input) which could be easily broken (returns null) if let’s say, the <input> tag is somehow moved to another table row (e.g. tr[8])

No matter which suggestion is better than which, at the end of the day, you are still in charge of being the final gatekeeper.

[2] XPath is not reliable

This impression comes from the fact that sometimes your test run fails on a different browser (say Firefox) because the element captured using XPath on the original browser (say Chrome) cannot be found. A closer look explains that the button’s XPath on Chrome is actually:


While on Firefox, that same button’s XPath is:


This could be the case of dynamically generated elements which is quite popular in web development nowadays. Somewhere along the way, a developer has tied the creation of these web elements to a specific web browser. Although this practice doesn’t promote testability, it still exists out there in the wild.

If you didn’t pay attention, you could blame XPath for doing a lousy job. But in fact, the culprit is not XPath. Instead, it is the application’s peculiar method of generating different IDs on different browsers. While waiting for the developers to fix this inconsistency and improve the app’s testability, you can continue testing by adding a little tweak to your XPath as follows:

//input[contains(@id, ‘stdinput-00001‘) or contains(@id, ‘stdinput-0000A‘)]

Bravo! Your test can now run on both Chrome and Firefox.

[3] XPath is the silver bullet

Relying on XPath too much without carefully dissecting your web application would result in a big frustration when you receive unexpected failures. It’s worth emphasizing that one single XPath can fetch different elements in different contexts. For instance, let’s examine the following XPath:


What you want to interact with is the below <a> element:

Open Store


But to your surprise, it’s not the case in reality. Instead, this element is the one being clicked:

Open Store


Therefore, to be absolutely sure about what you get, you should invest in a good insight of the page’s hierarchy. In this particular example, you can easily distinguish the two elements based on their different ancestors.

XPath for the product’s name of the search result (case #1):


XPath for the breadcrumb button (case #2):



Hopefully unveiling these deadly misconceptions could help you understand XPath on a deeper level so that your tests will become more robust and automating web apps is as effortless as possible. Stay tuned for the upcoming article dedicated to XPath’s best practices.

Why WordPress Asks for Connection Information?

Why WordPress Asks for Connection Information? To perform the requested action, WordPress needs to access your web server. Please enter your FTP credentials to proceed. If you do not remember your credentials, you should contact your web host.

Why WordPress asks for Connection Information?

Why WordPress Asks for Connection Information?

I sometime get this get this dialog asks for connection information, especially when sites hosted on VPS. I think this is because agent (apache or nginx) doesn’t have necessary permissions. In other words, you didn’t give right and/or enough permissions for agent.

How to Fix WordPress Ask for FTP Credentials?

It’s simple, give necessary permissions to agent. But what agent I should give permissions to? And how to it?

Outputs the username or agent that owns the running php/httpd process

cd /var/www/
sudo vi info.php

Access and you may see this screen


I am using NGINX & FPM. You may see it slightly different on your site like “apache” or similar if you use Apache.

Give permissions to agent

chown -R php-fpm /var/www/

Wrapping Up

That’s it. Quite simple huh? Don’t forget to delete info.php after finish. Leave your feedback or comment right below.

Setup LEMP Stack Centos 7 + NGINX + MariaDB + PHP 7 from Scratch

Believe me or not, there are hundred ways to setup web server. I searched and tried many of them on a VPS with 512MB of RAM. Most of them had issue with database, MySQL or MariaDB was die again and again. This one has worked for me for more than a year: CNMP (Centos 7 + NGINX + MariaDB + PHP 7).

Grab a VPS

I’ve used DigitalOcean as VPS provider for a while. Linode is one of my next try since its same price but double RAM. If you have any experience with other VPS providers, please share.

Basic Setup

Setup Firewall

Timezones Configuration

Create a Swap File

This section doesn’t apply for VPS at Linode since they have option to create SWAP file at VPS configuration.

LEMP (Linux, NGINX, MariaDB/MySQL, PHP) Stack Setup

You will see this page if you try to access to your server by using IP

Nginx Test Page


Setup Configuration for WordPress Site

Create root directory & Server Block folders

sudo mkdir /etc/nginx/sites-available
sudo mkdir /etc/nginx/sites-enabled
sudo mkdir -p /var/www/

Create the First Server Block File

sudo vi /etc/nginx/sites-available/vndeveloper.conf

Create a symlink

sudo ln -s /etc/nginx/sites-available/vndeveloper.conf /etc/nginx/sites-enabled/

Download WordPress source

cd ~
sudo unzip
sudo mv wordpress/* /var/www/

Create Database

mysql -u root -p
CREATE USER ‘dbuser’@’localhost’ IDENTIFIED BY ‘password’;
GRANT ALL PRIVILEGES ON dbname.* TO ‘dbuser’@’localhost’;

Wrap it up

That’s it. I know it’s too much if you are newbie to VPS but I hope it is helpful for you. This is how I setup Developer’s Notes (I didn’t cover ssl part yet). Leave a comment or feedback right below.