How to Deploy Django Applications on Linode (Ubuntu 20.04 LTS)

I recently deployed a Django application to Linode and had quite some trouble getting everything to run smoothly. Below I offer a step-by-step manual to get things running on a Ubuntu 20.04 LTS image.

Before you start

  • Make sure you have a functional Django app that you have tested with Django’s built-in development server.
  • Execute python3 manage.py makemigrations locally. (We’ll do the follow-up withmigrate in the production environment).
  • Push the complete project (e.g. mysite/)to a remote repository like GitHub or Bitbucket. Make sure you include the migrations folder in your repo (don’t put them in .gitignore !) and a requirements.txt file.

Setup Linode

Create a Linode account and create a new Linode (i.e. a Virtual Private Server):

  • Choose Distribution image: Ubuntu 20.04 LTS (which stands for “Long Term Support” and is more stable than 20.10)
  • Choose region: [pick something close to you]
  • Choose Linode Plan, Label and Root Password
  • Choose Optional Add-ons
  • Press “Create Linode”

When the Linode is created, go to the Network tab and take note of the following data, which we’ll need later on. I’ll be referring to these values as $LINODE_IP_ADDRESSand $HOSTrespectively.

  • Linode IP address (e.g. 119.162.191.84 )
  • Linode Reverse DNS (e.g. li2012-123.members.linode.com)

Change local settings.py

Re-organize your local settings.py file to easily switch from development to production settings, like so. (Take care to replace $HOST )

Connect via SSH

Connect to your Linode via SSH as root. (Take care to wait until your Linode virtual private server has booted and has the status “Running”):

ssh root@$LINODE_IP_ADDRESS

Take care: from now on, all commands will be executed on your Linode, unless explicitly stated otherwise!

Update apt

Update and upgrade the apt package manager in Ubuntu:

apt-get update && sudo apt-get upgrade -y

Install pip, Apache, mod-wsgi

Install pip3, apache2, and mod-wsgi(module used in Apache for running the Django server):

apt-get install python3-pip apache2 libapache2-mod-wsgi-py3

Update pip

python3 -m pip install --upgrade pip

Optimize Apache for Linode

Optimize Apache for Linode (this example caters to a 2GB RAM Linode) according to their guidelines. Make a backup of Apache’s configuration file and edit it:

cp /etc/apache2/apache2.conf /etc/apache2/apache2.backup.conf
nano /etc/apache2/apache2.conf

Add this to the end of the file:

KeepAlive Off   <IfModule mpm_prefork_module>
StartServers 4
MinSpareServers 20
MaxSpareServers 40
MaxClients 200
MaxRequestsPerChild 4500
</IfModule>
ServerName 127.0.0.1

Create software repository

Create a folder for your software repository. This will allow you to keep old versions that you can quickly revert to:

mkdir -p /root/repository/0.01git clone https://github.com/<username>/mysite.git /root/repository/0.01/mysite

Create virtual environment

Install virtualenv and verify the installation:

python3 -m pip install virtualenv
virtualenv --version

Make folders for your Django database and project, create a virtual environment in it and activate it. Also create a folder for your website itself.

mkdir -p /var/www/db
mkdir /var/www/mysite
cd /var/www/mysite
virtualenv myenv
cd myenv
source bin/activate
mkdir mysite

Install dependencies

Install the dependencies from the requirements.txt file in your GitHub repo within the virtual environment:

python3 -m pip install -r /root/repository/0.01/mysite/requirements.txt

Change remote settings.py

Let’s change our settings to PRODUCTION = True :

sed -i 's/PRODUCTION = False/PRODUCTION = True/g' /root/repository/0.01/mysite/mysite/settings.py

Apache config file

Disable the default Apache virtual host and add one for your site:

a2dissite *default
nano /etc/apache2/sites-available/mysite.conf

Replace the entire contents with the lines below. (Take care to replace $HOSTas above!) By the way: this file contains some Apache environment variables, which you can check with cat /etc/apache2/envvars

(Note: the configfile not only contains both 80 and 443 instances of the VirtualHost as a workaround for this certbotissue while trying to get a SSL certificate later on).

Check the configfile syntax by running:

apachectl configtest

Upload database

In my experience, it is best to start from an existing version of your Django database, which has some data in it. For me, this helped to solve a particularly persistent “500 Internal Server Error”. Moreover, it is is helpful to already have a Django “superuser” defined, if you want to test the “Admin” section in the production environment. (Again, this caused a 500 Error for me).

However, you should not put your development database in your remote repository. (You don’t ever want to risk overwriting your production database with the development version!) So for this one time, it’s better to use scp .

Execute this command in the appropriate, local folder!

scp db.sqlite3 root@$LINODE_IP_ADDRESS:/var/www/db

You’ll notice that I’m putting the database in a different folder than the application. Again, this will help prevent overwriting the production database when installing a new version of the application software.

Change ownership/permissions database

Go back to the Linode VPS and change the permissions so that the Django app can read your database and write to it:

chown www-data /var/www/db
chmod 664 /var/www/db/db.sqlite3
chown www-data /var/www/db/db.sqlite3

Install Django project

Time to install the Django software from your software repository:

cp -r /root/repository/0.01/mysite/ /var/www/mysite/myenv/
chown -R www-data /var/www/mysite/
python3 /var/www/mysite/myenv/mysite/manage.py migrate
python3 /var/www/mysite/myenv/mysite/manage.py collectstatic

Enable your site and reload Apache

a2ensite mysite
systemctl reload apache2

Go to $HOST URL in your browser

Head to $HOST_URL in your browser, where you should see a fully functional version of your Django website up and running!

New releases

After changing your Django software locally and pushing to GitHub you can use pieces of the above commands to whip up a one-sweep “install new release” script, like so:

Feedback?

I’m not an expert on Django and certainly not on web hosting or Apache, these steps are just what got things working for me. Any and all feedback is more than welcome!

Sources

Hi! 👋 I’m Tom. I’m a software engineer, a technical writer and IT burnout coach. If you want to get in touch, check out https://tomdeneire.github.io

--

--

Software engineer, technical writer, IT burnout coach

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store