Automating Deployment of a Django site with GitHub Actions

May 30, 2021

The background

Recently after I finished buiding an appointment scheduler in PHP1 without a framework (result: assignment-grade product), I’ve decided to learn Django for many reasons, being an SRE intern one of them. Going through a brilliant module on MDN2 gave me an awesome head start toward that end. Then I though of building an appointment scheduler again, but this time with Django in Python. While the experience so far has been great, in this post let’s only discuss about what’s in the title : )

The configuration

After setting up a server and getting our Django project onto a repo in GH (with a seperate prod branch), we can start automating the deployments right out from our repo. A workflow as follows shall get the job done without much hassle.

# This is a basic workflow to help you get started with Actions

name: Django site Deployment

# Controls when the action will run. 
on:
  # Triggers the workflow on push or pull request events but only for the prod branch
  push:
    branches: [ prod ]
  pull_request:
    branches: [ prod ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "deploy"
  deploy:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Saves SSH key to a file to be used by SSH
      - name: Save SSH key
        run: |
          mkdir -p ~/.ssh/
          echo "$SSH_KEY" > ~/.ssh/ssh_key
          chmod 600 ~/.ssh/ssh_key
          cat >>~/.ssh/config <<END
          Host django-server
            HostName $SERVER_IP
            User $SERVER_USERNAME
            IdentityFile ~/.ssh/ssh_key
          END
          echo $SSH_KNOWN_HOSTS > ~/.ssh/known_hosts          
        shell: bash
        env:
          SSH_KEY: ${{ secrets.SSH_KEY }}
          SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }}
          SERVER_USERNAME: ${{ secrets.SERVER_USERNAME }}
          SERVER_IP: ${{ secrets.SERVER_IP }}
        
      # Runs a set of commands using the runners shell
      - name: Update source at server
        run: |
          ssh django-server /bin/bash << EOF
            cd $PROJECT_PATH/
            ssh-agent bash
            ssh-add /home/$SERVER_USERNAME/.ssh/deploy_key
            git checkout prod
            git pull --ff-only
          EOF          
        shell: bash
        env:
          PROJECT_PATH: ${{ secrets.PROJECT_PATH }}
          SERVER_USERNAME: ${{ secrets.SERVER_USERNAME }}

Above workflow assumes that we’ve already met the following requirements,

  • Stored as GitHub repository secrets:

    • Private key $SSH_KEY
    • Django server’s ip $SERVER_IP
    • Django server’s username $SERVER_USERNAME
    • Django server’s public ssh identity in $SSH_KNOWN_HOSTS (StrictHostKeyChecking yes—IPs may float)
    • Path (i.e. absolute) of the repo clone directory in server
  • Public key of deploy_key added in GitHub repository deploy keys.

  • On Django Sever:

    • /home/$SERVER_USERNAME/.ssh/known_hosts has the public key of $SSH_KEY.
    • Public key of $SSH_KEY in /home/$SERVER_USERNAME/.ssh/authorized_hosts (perm: 640)
    • deploy_key in /home/$SERVER_USERNAME/.ssh/ (perm: 600)

It is worth noting that methods exist which require different configurations from the above, and pleasent surprises are waiting to be met with while exploring on one’s own.


  1. I like PHP : ) ↩︎

  2. https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django ↩︎

DjangoDeploymentAutomationGitHub

Uploading Artifacts in a Github Actions Workflow

Automating Deployment of a Hugo Site to GitHub Pages with GitHub Actions