If you are unfamiliar with Google Cloud Platform (GCP), you may be interested to know that GCP offers a free $300 credit when you first sign up. You can use this credit on any of the many Cloud computing services offered by them, but if you simply want to host a website, you can easily host almost any kind of website using this credit on their Compute Engine platform - without spending any money at all. With the range of options that GCP offers, this is definitely a really appealing option.
A bit of a disclaimer before we begin, we will be using the Comptue Engine platform in this tutorial, but there are actually other methods of deploying a Django Project on GCP, including one by using App Engine, which may actually be a better method than what is outlined here. However, I faced several issues when I tried it and just had a really hard time with it.
Hence, I decided to stick to what I was most familiar with, which was to rent a Virtual Machine using the Compute Engine platform and use that to host a website built with Django. This is the process that I will discuss in detail in this tutorial.
So I encourage you to do your own research to figure out if this is the best approach for you.
I have divided the tutorial in two parts. In Part 1 (this one), I will show you how to deploy a simple Django project on a GCP Compute Engine instance, which will be accessible through an IP address. In Part 2, I will show how to point a custom Domain name to this GCP instance, and do things like setting up an SSL certificate so you have "Https" instead of "Http"; and also redirect "non-www" to "www" version of the website.
As mentioned above, Corey Schafer's tutorial is an inspiration to this one, and I have tried to achieve similar things as were achieved in that tutorial.
Following are the 6 main sections of this article:
- Creating a Compute Engine instance
- Connecting to the instance
- Setting up the project files on the server
- Installing Apache & Mod WSGI
- Setting File Permissions
- Finishing Up
1. Creating a Compute Engine instance
The first step is to create an account at Google Cloud Platform and click "Console". Then from the menu on the left, go to Compute Engine > VM Instances. Then click Create Instance.
Set a name for your instance. Select a region and your Machine type:
You can see that I've named my instance "django-project". I've selected my region as "us-central1 (Iowa)", and my Machine type as "e2-medium".
You also need to select your operating system and your Disk Size. Also, make sure to Allow Http and Https Traffic:
I've chosen "Debian 10" as my operating system and a disk size of 100GB.
Now there is only one step left before you hit the "Create" button, which is to set up SSH keys that will allow you to connect to the server without a password.
Setting Up SSH
For this step, I'm going to use PuttyGen, which comes along with Putty, which is also something we will use later to SSH into the machine. So please go ahead and download it.
Open PuttyGen, set the bit rate as 4096, and hit Generate. You will have to move your mouse randomly in the area to get the result. After the key is generated, you can change the username by changing the "Key comment" field to something simpler. I've chosen "admin" as my username and my window looks like this:
You should now save the Private key in a folder of your local machine. You can choose to set a passphrase for it when prompted, but it is not required. Also copy the generated key and save it in a text file. After this, go back to the instance creation page.
Just before the Create button, there is a link: Management, security, disks, networking, sole tenancy, click to expand the options. In that, select the Security tab and paste in your generated key, which you had copied earlier.
With this, we are done setting up our instance. Review all the options above and hit the Create button.
After waiting for a few seconds, this is what my instance page looks like:
2. Connecting to the instance
Setup PuTTy
Now that we have created a Compute Engine instance, we have to connect to it. In this tutorial, I will use PuTTy, but you are free to use whichever software you prefer.
Open PuTTy and from the menu on the left, go to SSH > Auth. Click the Browse button and choose the Private key file you saved in the previous step.
Now go to the Session menu item, enter the External IP address of your instance, and click Open. You will get a prompt, asking if you trust the server. This is a prompt which you will always get when connecting to a new server. Click "Yes" and in the PuTTy command window, enter in your username (I had set mine as "admin"). You should be able to login with just your username and no password since we are using SSH.
Once you are logged in. Enter in the following commands to update and upgrade the installed packages:
Update and Upgrade packages
sudo apt-get update && sudo apt-get upgrade
You might have to enter "Y" in order to continue upgrading packages. Once this is done, you can keep the PuTTy window open as we will come back to it shortly.
The project
I've created a sample project, in my local machine that I will use for demonstration purposes in this tutorial. It is an extremely simple Django project that only has a Home Page view and this is what it looks like:
The following 2 images show the structure of the project.
- The project's name is
myproject
.
mp
is the virtual environment folder.
requirements.txt
contains the dependencies of the project.
static
contains the static files of the site like css, images and javascript.
media
is reserved for media files that may be uploaded.
You can generate the requirements.txt
file for your own project, using the following code:
output dependencies in a .txt file
pip freeze > requirements.txt
You will also have to add the following code to your my settings.py
to let Django know of the "static" and "media" folders:
Specify the "static" and "media" folders
import os
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
You can adapt the code and project structure according to your preferences.
I will be using a virtual environment when we deploy this project on the GCP instance and will use the requirements.txt
file to install dependencies in that virtual environment.
Setup FileZilla
I will be using FileZilla to upload the project files to the server. Here is how you can set that up.
Open FileZilla. Then go to Edit > Settings. From the SFTP menu item select Add Key and select the private key you saved earlier. Then hit Ok. This will load in the private key and allow us to connect to the server without a password.
Go to Site Manager in FileZilla. Create a New Site, and enter the IP address of your instance and the username. Like so:
Click Connect to connect to the server with your username and SSH keys. The directories should look like this:
3. Setting up the project files on the server
Uploading files to the server
First, create a directory in the home directory of the server. You can create it either using PuTTy or FileZilla. I did it using FileZilla and this is what it looks like:
Now enter the directory and upload the files from your local computer to this directory on the server. Make sure to exclude the virtual environment folder (mp
in my case).
Note: I was facing an issue with the upload of the db.sqlite3 file. I had to create a copy of the file, rename it to just "db", upload it to the server, and then rename that file to "db.sqlite3". You can try this technique if you face the same issue.
Now that our files are on the server, you can close FileZilla and switch to PuTTy.
Setting up the environment
Make sure you are logged into the server in PuTTy, just as we had left it in Section 2.
Now enter in the following commands to install pip
and venv
:
Install pip package manager
sudo apt-get install python3-pip
Installing venv: a virtual environment library
sudo apt-get install python3-venv
Make sure you're in the home directory of your user by entering pwd
:
If you're not then cd
into it.
Now create a virtual environment inside the myproject
folder (Obviously you would change this to your own project name):
Initialize a virtual environment "mp"
python3 -m venv myproject/mp
Next change directory into the myproject
folder:
cd into the project's directory
Now activate the virtual environment
Activate the virtual environment
Next, install the dependencies that are listed in the requirements.txt
file
Installed the dependencies listed in the requirements.txt file
pip install -r requirements.txt
Now we need to add the instance's IP address in the settings.py
file of our project. You can either do this using nano
as below or through FileZilla.
This will allow you to edit the "settings.py" in the terminal (PuTTy)
nano myproject/settings.py
Then finally, use the following command to collect the static files in the static
folder.
Collecting the static files in the "static" folder.
python manage.py collectstatic
With this, we are done with the basic preparation for launching our Django. Next, we need to set up a Webserver so we are able to serve the Django application.
4. Installing Apache & Mod WSGI
We will be installing the popular Apache 2 webserver for our project. We will also install the Mod_WSGI module that will allow us to host our python-based web application under Apache.
Install Apache2 Webserver and Mod_WSGI interface
sudo apt-get install apache2 && sudo apt-get install libapache2-mod-wsgi-py3
You might be prompted to confirm the installation. Enter "Y", whenever necessary.
The next few steps that remain in this section of the tutorial can be a little hard to understand in the beginning.
All that we are trying to do in these steps is to let Apache know the location of our project files, and how we'd like our project to be configured.
First, let'scd
into the sites-available
directory.
cd to the "sites-available" directory
cd /etc/apache2/sites-available
This folder has a default configuration file named 000-default.conf
. So instead of creating our configuration file from scratch, we will use this file.
The following code will copy the 000-default.conf
and create a duplicate of it named myproject.conf
.
Creating our configuration file using the default one
sudo cp 000-default.conf myproject.conf
Now use nano
to edit this file.
Editing the newly created configuration file
Now, you can analyze the following code and edit the file yourself. Or simply delete all the lines of the file by holding CTRL+K and paste in the following code:
<VirtualHost *:80>
# The ServerName directive sets the request scheme, hostname and port that
# the server uses to identify itself. This is used when creating
# redirection URLs. In the context of virtual hosts, the ServerName
# specifies what hostname must appear in the request's Host: header to
# match this virtual host. For the default virtual host (this file) this
# value is not decisive as it is used as a last resort host regardless.
# However, you must set it for any further virtual host explicitly.
#ServerName www.example.com
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the loglevel for particular
# modules, e.g.
#LogLevel info ssl:warn
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# For most configuration files from conf-available/, which are
# enabled or disabled at a global level, it is possible to
# include a line for only one particular virtual host. For example the
# following line enables the CGI configuration for this host only
# after it has been globally disabled with "a2disconf".
#Include conf-available/serve-cgi-bin.conf
Alias /static /home/admin/myproject/static
<Directory /home/admin/myproject/static>
Require all granted
</Directory>
Alias /media /home/admin/myproject/media
<Directory /home/admin/myproject/media>
Require all granted
</Directory>
<Directory /home/admin/myproject/myproject>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
WSGIScriptAlias / /home/admin/myproject/myproject/wsgi.py
WSGIDaemonProcess django_app python-path=/home/admin/myproject python-home=/home/admin/myproject/mp
WSGIProcessGroup django_app
</VirtualHost>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
The above code defines the filesystem structure of our project and also set permissions for Apache to open files and folders from the specified locations.
You simply have to replace bits of this code to match your setup. More specifically, you will have to replace myproject
with your project's name, admin
with your username, and lastly mp
with the name of your virtual environment folder.
Save the file and exit.
Now, we simply need to enable the new site, and disable the default one:
Enable the "myproject" site (one defined by "myproject.conf")
Disable the default site
sudo a2dissite 000-default.conf
With this, we are done setting up our server and the configurations for our project. All that's left to do now is setting the right permissions to our files and folders.
5. Setting File Permissions
cd
into the home directory (You can just enter cd
). Use the pwd
command to confirm your location:
Enter in the following commands, one at a time.
Setting the right permissions
sudo chown www-data:www-data myproject/
sudo chown :www-data myproject/db.sqlite3
sudo chmod 664 myproject/db.sqlite3
sudo chown -R :www-data myproject/media
sudo chmod -R 775 myproject/media
The first two lines assign ownership of the myproject
folder and db.sqlite3
to the www-data
user and group. The third line modifies the permissions of the db.sqlite3
file and makes it writable. This will now allow you to not only read but also write to the database.
The fourth line gives ownership of the media
folder to the www-data
group of Apache and the last line modifies the permissions of the folder appropriately. The -R
flag does this recursively (for any subfolders and files as well).
Now simply restart Apache.
Restart the apache2 service
sudo service apache2 restart
Now if you visit the IP address of your instance, your Django application should be working as expected, as is mine:
Hurray!
6. Finishing Up
One of the things mentioned in the Django deployment checklist is to keep sensitive information like email addresses, passwords, and the SECRET_KEY
that are usually defined in the settings.py
file, outside of the project files, but still be accessible.
One way to do this is to store this information in a file on the server, and simply load this file in the settings.py
and use the information stored in there.
The first step to achieve this is to open your settings.py
file to be edited. You can either do it through nano
inside PuTTy, or use FileZilla to edit the file using your own text editor locally. This time I have used FileZilla and Sublime Text to edit this file:
Copy the "SECRET_KEY" and either keep it in your clipboard or save it in a temporary file on your local computer. Then add the following code in the settings.py
file.
Load config.json file and use the stored SECRET_KEY
import json
with open('/etc/config.json') as config_file:
config = json.load(config_file)
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = config["SECRET_KEY"]
The code above will load the config.json
file (which we are about to create) and save its contents in the config
variable. We then define the "SECRET_KEY" usingconfig["SECRET_KEY"]
.
Reminder: You should also set your DEBUG
variable to False
whenever you are done debugging.
Create config.json using the touch command
sudo touch /etc/config.json
Open this file for editing using nano
sudo nano /etc/config.json
Paste in the "SECRET_KEY" that you copied from the settings.py
file earlier and save it in the JSON format.
This is what mine looks like:
Save the file and exit.
Now you can restart the apache2
service and reload the page in your browser, and it should run just as it did before, but now you no longer should have any sensitive information directly written in your settings.py
file.
Restart Apache
sudo service apache2 restart
Please test all the functionality of your application, including logging in to your admin, and creating and editing content. If you run into any problems, review Section 4 and 5 of this tutorial carefully, and make sure you haven't made any mistakes modifying the code to your own specifications.
Hopefully, you were able to follow along without much trouble.
In Part 2, I will show you how to point a domain name to your instance, set up an SSL certificate to use HTTPS instead of the HTTP protocol, and also how to redirect your website from non-www to www version of your site.
Thank you for reading!