Django Continuous Deployment to PythonAnywhere
Tags: devops, tutorial, django, productivity
Published: 2019-10-17 22:38:15

---
description: A tutorial on setting up Django CI/CD to PythonAnywere.
---

This is a streamlined version of my original [Python Django](https://semaphoreci.com/blog/python-continuous-integration-continuous-delivery) tutorial. Here, I want to focus on the continuous deployment stage, so you may get your website online quicker. If, after reading this, you are curious about multi-branch deployments and continuous integration, be sure to check out the original article. I hope you enjoy it. 🍭

In this hands-on guide, we're going to learn how to deploy a Django website to [PythonAnywhere](https://www.pythonanywhere.com/) using [Semaphore](https://semaphoreci.com) Continuous Integration and Delivery (CI/CD). No matter how reliable the code is, we still need to implement CI/CD to detect and remedy errors quickly. When we have confidence in the accuracy of our code, you can ship updates faster and with fewer mistakes.

## Things You’ll Need

You’ll need [GitHub](https://github.com) and [Semaphore](https://semaphoreci.com) accounts. I’d recommend installing Semaphore’s [commad line tool](https://docs.semaphoreci.com/article/53-sem-reference) for an easy setup.
    
To get started, fork, clone the [Django demo project](https://github.com/semaphoreci-demos/semaphore-demo-python-django) and add it to Semaphore:

```bash
$ cd semaphore-demo-python-django
$ sem init
```

The application you just forked is a simple task manager; we can create and edit tasks, we also have a separate admin site to manage users and permissions. The website is built with Python and Django. The data will be stored in MySQL.

## A Note About Continuous Integration

[Continuous Integration](https://semaphoreci.com/continuous-integration) is a programming discipline in which the application is built and tested each time there is a push. By making multiple small changes instead of a big one, problems are detected earlier and corrected faster. Such a paradigm, clearly, calls for an automated system to carry out all the steps. In such systems, code travels over a path—a pipeline—and it must pass an ever-growing number of tests before it can reach the users.

The project includes a complete CI pipeline with a complete suite of tests. No setup required:

- Unit tests with [nose](https://nose.readthedocs.io/en/latest/) and [coverage](https://coverage.readthedocs.io/en/v4.5.x/).
- Static code analysis with [pylint](https://www.pylint.org/).
- In-browser tests with [selenium](https://www.seleniumhq.org/).

The in-depth explanation about the CI stage can be found on the [original full-length tutorial](https://semaphoreci.com/blog/python-continuous-integration-continuous-delivery)

## Deploy to PythonAnywhere

Websites are meant to run on the internet; let see how we can publish our site the world to enjoy. PythonAnywhere is a hosting provider that, as the name suggests, specializes in Python. In this section, we'll learn how to use it.

### Create the Website


Head to [PythonAnywhere](https://pythonanywhere.com) (PA) and create an account. In order to do an automated deployment, we need SSH access, and that requires a paid account, so sign up for the entry-level tier (hacker 🎩).

Once you have your account, create the services for the application:

1. Create a MySQL database called `pydjango_production` with a good password.
1. Generate an [API Token](https://help.pythonanywhere.com/pages/API).
1. Add your public SSH key to PA servers:

```bash
$ ssh-copy-id YOUR_PA_USERNAME@ssh.pythonanywhere.com
```

Create an environment file for your application: `.env`

``` bash
# This value is found on PythonAnywhere Accounts->API Token.
export API_TOKEN=YOUR_API_TOKEN

# Django Secret Key - Use a long random string for security.
export SECRET_KEY=A_RANDOM_STRING_FOR_DJANGO

# These values can be located on PythonAnywhere Databases tab.
export DB_HOST=YOUR_PA_USERNAME.mysql.pythonanywhere-services.com
export DB_USER=YOUR_PA_USERNAME
export DB_PASSWORD=YOU_DB_PASSWORD
# The name of the DB is prefixed with USERNAME$
export DB_NAME='YOUR_PA_USERNAME$pydjango_production'
export DB_PORT=3306
```

Scp the file to the server:

```bash
$ scp .env YOUR_PA_USERNAME@ssh.pythonanywhere.com:~
```

Now we're ready to create the website. Luckily for us, there is an official helper script. If you wish to use a custom domain instead of the default one (USERNAME.pythonanywhere.com), add a `--domain=` option.
 
``` bash
$ ssh YOUR_PA_USERNAME@ssh.pythonanywhere.com
$ source ~/.env
$ pa_autoconfigure_django.py --python=3.7 YOUR_GITHUB_REPO_URL
```

The script should take a few minutes to complete. Take a cup of coffee, and don't forget to stretch 🧘🏽.

Once done, there’s only one thing left to do. We need the app to have access to our environment file. Edit the WSGI file for your new website, by default it’s located at `/var/www`. Add three lines to that file, as shown below:

``` python
# This file contains the WSGI configuration required to serve up your
# Django app
import os
import sys

# Add your project directory to the sys.path
settings_path = '/home/YOUR_PA_USERNAME/YOUR_PA_USERNAME.pythonanywhere.com'
sys.path.insert(0, settings_path)

# Set environment variable to tell django where your settings.py is
os.environ['DJANGO_SETTINGS_MODULE'] = 'pydjango_ci_integration.settings'

# -–-> ADD THESE NEXT THREE LINES TO YOUR WSGI.PY <-------
from dotenv import load_dotenv
env_file = os.path.expanduser('~/.env')
load_dotenv(env_file)
# --------------------------------------------------------

# Set the 'application' variable to the Django wsgi app
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
```

That’s it, after reloading the website, it should be online 🥇.

## Benefits of Continuous Deployment

Deployment is a complex process with a lot of moving parts. It would be a shame if, after painstakingly testing everything, the application crashes due to a faulty deployment.

[Continuous Deployment](https://semaphoreci.com/cicd) (CD) is an extension of the CI concept; in fact, most integration tools don't make a great distinction between CI and CD. A CD pipeline performs all the deployment steps as a repeatable, battle-hardened process.

### Deployment with Semaphore

We’re going to create a new [pipeline](https://semaphoreci.com/blog/cicd-pipeline) to deploy the updates automatically on each update.

The deployment process needs some secret data, for example, the SSH key to connect to PythonAnywhere. The environment file also has sensitive information, so we need to protect it.

Storing [secrets](https://docs.semaphoreci.com/article/66-environment-variables-and-secrets) in Semaphore is easy as pie:

```bash
$ sem create secret ssh-key \
    -f $HOME/.ssh/id_rsa:/home/semaphore/.ssh/id_rsa_pa
```

Now do the same for the environment file:

```bash
$ sem create secret env \
    -f ./.env:/home/semaphore/.env
```

Create a new pipeline file at `.semaphore/deploy.yml` with the next three code boxes:

``` yaml
version: v1.0
name: Deploy Django to PythonAnywhere
agent:
  machine:
    type: e1-standard-2
    os_image: ubuntu1804
```

The pipeline has only one “Deploy” block. It begins by invoking the secrets we just created: env and ssh key. We can also define environment variables at this point, adjust the values for your user:

``` yaml
blocks:
  - name: Deploy
    task:
      secrets:
        - name: env
        - name: ssh-key
      env_vars:
        - name: SSH_USER
          value: YOUR_PA_USERNAME
```

The job uses [checkout](https://docs.semaphoreci.com/article/54-toolbox-reference#checkout) to clone the repository, then [envsubst](https://linux.die.net/man/1/envsubst) expands the values of `deployment.sh` with the corresponding environment values. Finally, the files are copied to PA and the deployment script executed remotely:

``` yaml
      jobs:
        - name: Deploy to PythonAnywhere
          commands:
            - checkout
            - cat deployment.sh | envsubst > ~/deploy.sh
            - chmod 0600 ~/.ssh/id_rsa_pa
            - ssh-keyscan -H ssh.pythonanywhere.com >> ~/.ssh/known_hosts
            - ssh-add ~/.ssh/id_rsa_pa
            - scp ~/.env ~/deploy.sh ${SSH_USER}@ssh.pythonanywhere.com:~
            - ssh ${SSH_USER}@ssh.pythonanywhere.com bash deploy.sh
```

I mentioned `deployment.sh`, but haven't shown it yet. Here it is:

``` bash
# pull updated version of branch from repo
cd ${SSH_USER}.pythonanywhere.com
git fetch --all
git reset --hard origin/$SEMAPHORE_GIT_BRANCH

# perform django migration task
source ~/.env
source ~/.virtualenvs/${SSH_USER}.pythonanywhere.com/bin/activate
python manage.py migrate

# restart web application
touch /var/www/${SSH_USER}_pythonanywhere_com_wsgi.py
```

In short, the script does 3 things:

1. Updates the app code from the repository.
2. Executes `manage.py migrate`, in case there new code has additional tables.
3. Restarts the web application.

Now all that remains is to link the pipelines. This is achieved adding a [promotion](https://docs.semaphoreci.com/article/67-deploying-with-promotions) to the end of `.semaphore/semaphore.yml`:

``` yaml
promotions:
  - name: Deploy
    pipeline_file: deploy.yml
```

Push all the updated files to your repository:

``` bash
$ git add .semaphore/*
$ git add deployment.sh
$ git commit -m "add deployment"
$ git push origin master
```

Semaphore will start working immediately. Wait until the CI pipeline is done:

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/8uravgto9ifct0byrb1f.png)

And hit the **Promote** button to start the deployment:

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/az9u1pvpjhun5irdc05e.png)

Enjoy your new website.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/45foemev0pcj5t6f9sg1.png)

## Conclusion

We've discovered the incredible potential of a CI/CD platform. I hope that the tools and practices discussed here can add value to your projects, improve your team effectiveness, and make your life easier.

For the next steps, I suggest browsing [Semaphore Blog]( https://semaphoreci.com/blog) for more examples and tutorials, and, of course, writing a pipeline for your own application. Good luck!

Did you find the post useful? Let me know by ❤️-ing or 🦄-ing below! Do you have any questions or suggestions for other tutorials? Let me know in the comments. Thank you for reading!