Tristan Perry

How To: Setup a Server and Deploy Django Python 3 App From Scratch

When deploying the new version of Plagiarism Guard, I didn’t want to disturb the existing install. So instead I decided to order a new server (in this case a VPS) and get everything setup on the new server first, before switching over the DNS. This post details the steps I took to setup the basics of the server and deploy the Django application.

NB: Plagiarism Guard is written by Python 3 so I decided to boot the server as Ubuntu 14.04, which comes with both Python 2.7 and Python 3.4 installed by default. It wouldn’t be much more work to choose a Python 2-only distro and setup Python 3 via virtualenv, although for a brand new box it seemed sensible to choose a recent Linux distro which comes with Py3 by default. Firstly I logged into the server via SSH using PuTTY as a client. Your server/VPS provider will provide you with an IP address when they have provision the server. If they don’t specify an SSH port, it’ll probably be 22.

This article was written in late 2014, and whilst a lot of the commands and advise in this article are still relevant, a couple of links might be to an old ‘end of line’ version of software

Initial Server Setup

To speed up the server config I decided to firstly install Webmin/Virtualmin which is a free server control panel. In comparison to cPanel/WHM, Webmin is like WHM and Virtual is like cPanel. To install this I typed the following commands into the SSH window:

cd /

wget http://software.virtualmin.com/gpl/scripts/install.sh

chmod +x install.sh

./install.sh

Using the install script you shouldn’t be prompted for any input mid-installation, but keep an eye on the SSH window in-case they ask you to provide any information. Once complete, go to https://[serverip]:10000 in your browser. At this stage you will be shown an ‘Invalid SSL certificate’ browser warning which is safe to ignore for now. On first login, you will be prompted to follow a post installation wizard. It’s fairly self explanatory so just choose settings relevant to your requirements. I tend to disable email anti-virus unless the server has over 1 GB RAM. I also choose an alternative SSH port, something above 10000. This is because some common hacking scripts will try to break into your server’s SSH by using common passwords and going to port 22. By using a non standard port, you are slightly increasing security of your box. On the topic of security, a great free software firewall is CSF which can be installed with the following commands:

cd /usr/local/src
wget http://www.configserver.com/free/csf.tgz
tar –xvf csf.tgz
cd csf
sh install.sh

CSF has a GUI via Webmin which can be easily activated as per the installation instructions:

To install or upgrade the csf webmin module: Install csf as above Install the csf webmin module in:

Webmin > Webmin Configuration > Webmin Modules > From local file > /usr/local/csf/csfwebmin.tgz > Install Module

Once installed and activated within Webmin, go to Webmin -> System -> ConfigServer Security & Firewall -> Firewall Configuration. Firstly set TESTING to 0. Then verify that the alternative SSH port you specified above is listed under both TCP_IN and TCP_OUT and remove port 22 if it’s listed (obviously only do this if you chosen to change your SSH port from 22!). Since my application is written in Python 3 and will be powered by PostgreSQL and Django, I run the following commands to configure Python and Apache as required:

apt-get update

apt-get install libapache2-mod-wsgi-py3

apt-get install nano

apt-get install python3-pip

apt-get install python-psycopg2

pip3 install psycop2

Django is run via mod-wsgi and since I’m running Python 3, I installed this dependency via the ‘apt-get install libapache2-mod-wsgi-py3’ command. At this point, you can install any Python packages you need by running pip3 install [package name], e.g.:

pip3 install Django==1.7

If you haven’t been keeping track of your project’s Python dependencies, you can run ‘pip list’’ or similar on your current dev environment.

Publishing/Installing Your Website

Firstly add your website via Virtualmin -> Create Virtual Server and filling out the relevant options. This creates the relevant Zone files (DNS), Apache2 configuration files and a directory for your website files under /home. To setup email accounts on this domain, select your newly created virtual server and click ‘Edit Users’. Click “Add a user to this server.” and choose the relevant settings. The tricky bit with installing a Django app for the first time is knowing where to put your website files and how to configure it under Apache2. I found the guides by Linode and Django to be useful but hopefully the below advise will also prove useful. Firstly, only your static files (images, Javascript, CSS etc) need to be uploaded to your /public_html directory. The rest of your files can then be uploaded under your /home/[username] directory. By installing Webmin you will have FTP ready to go so login to FTP via a client such as Filezilla (using the provided IP address, port 21 and the chosen username and password when creating this virtual server). You can see how I uploaded my Django app’s files in the following screenshot (click to see full version):

File structure of Django files

As can be seen, my Django manage.py file was uploaded into the /home/[username] folder whereas the Django apps and settings folder (e.g. /PlagiarismGuard and /plag) was uploaded into sub-folders of /home/[username]/. Only my static files were uploaded into /home/[username]/public_html since these are the only parts of a Django app that need to be publicly facing. You will probably also need to edit your wsgi.py file to add your site’s path into Python:

nano /home/plagiarismguard/PlagiarismGuard/wsgi.py

And I added:

import sys
sys.path.insert(0, '/home/plagiarismguard/')

After uploading the files I went into the settings.py file and made the following changes:

  • Changed DEBUG to: False
  • Changed TEMPLATE_DEBUG to: False
  • Changed ALLOWED_HOSTS to: [‘*’]
  • Changed DATABASES to match the PostgreSQL database information (go to ‘Edit Databases’ under Virtualmin):
DATABASES = {
  'default': {
  'ENGINE': 'django.db.backends.postgresql_psycopg2',
  'NAME': 'database_name',
  'USER': 'username',
  'PASSWORD': 'password',
  'HOST': '127.0.0.1',
  'PORT': '5432',
  }
}
  • Changed MEDIA_ROOT to the location where I will be storing any file uploads (in this case, /home/plagiarismguard/media/)

At this point you have uploaded your Django files and configured the environment so that Python and WSGI should serve your app as expected. The final step involves needing to modify the Apache2 conf file (in Ubuntu, this is under /etc/apache2/sites-enabled) so that it’s aware that it should serve your Python files via WSGI. The Django guide on this is very useful and I’ve also copy & pasted my final conf file at the end of this post. If you’ve never had experience with Apache2 configuration, this can be tricky, but the main thing to add is the WSGIScriptAlias command and ‘Require all granted’ for wsgi.py in the relevant . When complete, restart Apache2 with:

service apache2 restart

If you get any errors when visiting your site via the IP address, check your site’s error_log with:

tail -f /home/[username]/logs/error_log

If the error is not useful, you can also try temporarily setting DEBUG to True in settings.py (you will need to restart Apache2 for this change to take affect) and you’ll (hopefully) get a useful Django/Python error stack. If the error is something like:

ImportError: Could not import settings ‘settings.py’ (Is it on sys.path? Does it have syntax errors?): No module named settings.py.

Then re-check that you have correctly added your site’s path into wsgi.py as mentioned earlier. Overall though the steps described above should be sufficient to get your website up and running. Congratulations! You can verify that it works as expected by going to your virtual host’s IP address in your browser.

Change DNS

Once your server is setup as expected, you will need to setup the nameservers and change the domain’s DNS. Firstly go to Webmin -> BIND DNS Server -> Select site under ‘Existing DNS zones’ -> Address. Then add two nameservers (ns1.yourdomain.com. and ns2.yourdomain.com. - remembering the trailing period - and point them to your IP address(es). Then setup these nameserver/IP records at your domain name registration company. Methods for doing this vary but the guide for doing this on GoDaddy can be seen here.

Then change your domain’s nameservers to ns1.yourdomain.com and ns2.yourdomain.com and after a short wait (usually 1-12 hours), your website should resolve when going to yourdomain.com.

Optional: Migrate the Database

If you are starting from a fresh database (as configured in settings.py), you can simply run:

cd /home/[username]

python3 manage.py syncdb

To create a fresh database. However if you are looking to migrate data as well, I found the tutorial by Matthew Wittering to be useful.

Optional: Add an SSL Certificate

To setup SSL certificates via Virtualmin, you can:

  • Go to Virtualmin -> Server Configuration -> Manage SSL Certificate -> Create Signing Request
  • After generating a CSR, buy an SSL certificate with your favourite SSL provider. They will email you instructions but part of it will involve sending them the CSR generated above. Never send out the private key which is generated at the same time.
  • After a short wait, the SSL provider will send you your SSL certificate. Copy and paste the SSL key into Virtualmin -> Server Configuration -> Manage SSL Certificate -> Update Certificate and Key -> Signed SSL Certificate -> Pasted Text
  • If you purchased a less known type of SSL certificate (some of which give errors, especially on mobile devices and tablets), you might be sent an ‘intermediate’ certificate. You can upload this to Virtualmin -> Server Configuration -> Manage SSL Certificate -> CA Certificate.

If you want all visitors to see the SSL (https) version of your website, you can change the :80 (http) Apache2 config to:

<VirtualHost [your IP address]:80>
RewriteEngine on
RewriteCond %{HTTPS} off
RewriteRule (.*) https://www.[yourdomain].com%{REQUEST_URI} [R=301,L]
</VirtualHost>

Optional: Redirect Old URLs

If your Django/Python application replaces another website, you should redirect any old URLs to your new ones to ensure that your visitors and the search engines don’t get confused. To do this, you can enable RewriteEngine in Apache2’s configuration and redirect your URLs as applicable:

RewriteEngine on
Redirect 301 /Features.php https://www.plagiarismguard.com/features-screenshots/

Optional: Also Run PHP Applications

If you also want to run PHP applications via this virtual host, you can upload the site into the relevant folders (e.g. /home/[username]/public_html/php-app) and then insert the following lines into your site’s Apache2 conf file:

Alias /[php-app] /home/[username]/public_html/[php-app]/

<Directory /home/[username]/public_html/[php-app]>
  Options -Indexes +IncludesNOEXEC +SymLinksIfOwnerMatch +ExecCGI
  Require all granted
  AddHandler fcgid-script .php
  FCGIWrapper /home/[username]/fcgi-bin/php5.fcgi .php
  DirectoryIndex index.php
</Directory>

Future Server Configuration

This is a basic guide covering an initial server setup; you will probably want to make further changes to your newly configured box. Feel free to search around Webmin and the internet if you are unsure how to make certain changes. One key bit of advice after all this is complete is to update your system. You can either do this via Webmin -> System Information -> Virtualmin Package Updates or by running:

apt-get update

apt-get upgrade

Final Apache2 Configuration File for Plagiarism Guard

This configuration file redirects http:// requests to https://, non-www requests to www and also redirects old page URLs to their new forms. I’m also running two PHP applications (/blog which is powered by WordPress and /support which is powered by OSTicket).

cat /etc/apache2/sites-enabled/plagiarismguard.com.conf

<VirtualHost 107.191.102.183:80>
  RewriteEngine on
  RewriteCond %{HTTPS} off
  RewriteRule (.*) https://www.plagiarismguard.com%{REQUEST_URI} [R=301,L]
</VirtualHost>

<VirtualHost 107.191.102.183:443>
  SuexecUserGroup "#1000" "#1000"
  ServerName plagiarismguard.com
  ServerAlias www.plagiarismguard.com

  DocumentRoot /home/plagiarismguard/public_html
  ErrorLog /var/log/virtualmin/plagiarismguard.com_error_log
  CustomLog /var/log/virtualmin/plagiarismguard.com_access_log combined

  ScriptAlias /cgi-bin/ /home/plagiarismguard/cgi-bin/
  WSGIScriptAlias / /home/plagiarismguard/PlagiarismGuard/wsgi.py

  <Directory /home/plagiarismguard/PlagiarismGuard>
    
      Require all granted
    
  </Directory>

  <Directory /home/plagiarismguard/cgi-bin>
    allow from all
    AllowOverride All Options=ExecCGI,Includes,IncludesNOEXEC,Indexes,MultiViews,SymLinksIfOwnerMatch
    Require all granted
  </Directory>

  Alias /robots.txt /home/plagiarismguard/public_html/robots.txt
  Alias /sitemap.xml /home/plagiarismguard/public_html/sitemap.xml
  Alias /favicon.ico /home/plagiarismguard/public_html/static/plag/img/favicon.png
  Alias /blog /home/plagiarismguard/public_html/blog/
  Alias /support /home/plagiarismguard/public_html/support/
  Alias /static/ /home/plagiarismguard/public_html/static/

  <Directory /home/plagiarismguard/public_html/static>
    Require all granted
  </Directory>

  <Directory /home/plagiarismguard/public_html>
    <Files ~ "(robots.txt|sitemap.xml)">
      Require all granted
    </Files>
  </Directory>

  <Directory /home/plagiarismguard/public_html/blog>
    Options -Indexes +IncludesNOEXEC +SymLinksIfOwnerMatch +ExecCGI
    Require all granted
    AddHandler fcgid-script .php
    FCGIWrapper /home/plagiarismguard/fcgi-bin/php5.fcgi .php
    DirectoryIndex index.php

    
      RewriteEngine On
      RewriteBase /blog/
      RewriteRule ^index\\.php$ - [L]
      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteCond %{REQUEST_FILENAME} !-d
      RewriteRule . /blog/index.php [L]
    
  </Directory>

  <Directory /home/plagiarismguard/public_html/support>
    Options -Indexes +IncludesNOEXEC +SymLinksIfOwnerMatch +ExecCGI
    Require all granted
    AddHandler fcgid-script .php
    FCGIWrapper /home/plagiarismguard/fcgi-bin/php5.fcgi .php
    DirectoryIndex index.php
  </Directory>

  RewriteEngine on
  RewriteCond %{HTTP_HOST} !^www\\. [NC]
  RewriteRule ^(.*)$ https://www.%{HTTP_HOST}$1 [R=301,L]
  Redirect 301 /Features.php https://www.plagiarismguard.com/features-screenshots/
  Redirect 301 /Pricing.php https://www.plagiarismguard.com/pricing/
  Redirect 301 /Plagiarism.php https://www.plagiarismguard.com/risks-of-plagiarism/
  Redirect 301 /Company.php https://www.plagiarismguard.com/about-us/
  Redirect 301 /Order.php https://www.plagiarismguard.com/order/
  Redirect 301 /TOS.php https://www.plagiarismguard.com/terms-of-service/
  Redirect 301 /PrivacyPolicy.php https://www.plagiarismguard.com/privacy-policy/

  RemoveHandler .php
  RemoveHandler .php5
  php_admin_value engine Off
  IPCCommTimeout 31
  FcgidMaxRequestLen 1073741824

  SSLEngine on
  SSLCertificateFile /home/plagiarismguard/ssl.cert
  SSLCertificateKeyFile /home/plagiarismguard/ssl.key
  SSLCertificateChainFile /home/plagiarismguard/ssl-bundle.txt
</VirtualHost>