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 arequirements.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_ADDRESS
and $HOST
respectively.
- 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 $HOST
as 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 certbot
issue 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
- Host a Website on Ubuntu 18.04 (Linode, Ubuntu 18.04 LTS)
- Deploying to a Server — Django Tutorial (Linode, Ubuntu 18.04 LTS)
- How to Deploy Django Applications on AWS EC2 Using Apache server (AWS EC2, Ubuntu 18.04 LTS)
- How to Deploy Django Applications on AWS EC2 Using Apache server (AWS EC2, Ubuntu 20.04 LTS)
- How to Update Your Live Django Website
- How to use Django with Apache and mod_wsgi
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