A step-by-step guide to deploying a Django website to PythonAnywhere with automatic deployment via GitHub Actions.
π Project Overview
Goal: Create a personal website with:
- Django backend
- Custom domain (optional)
- HTTPS security
- Automated deployment from VS Code to production
Tech Stack:
- Django 5.2.8+
- PythonAnywhere hosting
- GitHub Actions for CI/CD
- Custom domain with SSL (optional)
Result: Push code from VS Code β Site updates automatically in production
Example project: This tutorial uses my_website as the project name. Replace it with your own project name throughout.
π― Prerequisites
Before starting, you need:
- Python 3.10+ installed
- VS Code (or any code editor)
- GitHub account
- PythonAnywhere account (Hacker tier for custom domains)
- Custom domain (optional)
π Step-by-Step Guide
Step 1: Create Django Project Locally
# Create project directory (use your own project name)
mkdir my_website
cd my_website
# Create virtual environment
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# Install Django
pip install django
# Create Django project (replace 'my_website' with your project name)
django-admin startproject my_website .
django-admin startapp main_app
# Create requirements.txt
pip freeze > requirements.txt
# Test locally
python manage.py runserver
Visit http://127.0.0.1:8000 to verify Django works.
Note: Throughout this tutorial:
-
my_website= your Django project name -
main_app= your Django app name - Replace these with your actual names
Step 2: Setup Git and GitHub
# Initialize Git
git init
git add .
git commit -m "Initial commit"
# Create repository on GitHub
# Then connect local to remote:
git remote add origin https://github.com/YOUR_USERNAME/my_website.git
git push -u origin main
Important: Create a .gitignore file:
*.pyc
__pycache__/
db.sqlite3
venv/
.env
Step 3: Configure Django for Production
Edit my_website/settings.py (replace my_website with your project name):
# Add this at the top
from pathlib import Path
import os # CRITICAL - don't forget this!
# Configure allowed hosts (replace with your domain)
ALLOWED_HOSTS = [
'yourdomain.com', # Your custom domain
'www.yourdomain.com', # www version
'YOUR_USERNAME.pythonanywhere.com' # PythonAnywhere subdomain
]
# For production, set DEBUG = False (but keep True during setup)
DEBUG = True # Change to False after everything works
# Add your app to INSTALLED_APPS
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'main_app', # Your app name here
]
# Static files
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
Step 4: Deploy to PythonAnywhere
4.1 Setup on PythonAnywhere
-
Create account at pythonanywhere.com
- Free tier works for testing
- Hacker tier ($5/month) needed for custom domains
Open Bash console and clone your repo:
# Replace YOUR_USERNAME and my_website with your values
git clone https://github.com/YOUR_USERNAME/my_website.git
cd my_website
- Create virtual environment:
# Replace my_website_env with your preferred name
mkvirtualenv --python=/usr/bin/python3.10 my_website_env
pip install -r requirements.txt
-
Configure Web App:
- Go to Web tab
- Add new web app
- Choose "Manual configuration"
- Python 3.10
- Set source code:
/home/YOUR_USERNAME/my_website - Set working directory:
/home/YOUR_USERNAME/my_website - Set virtualenv:
/home/YOUR_USERNAME/.virtualenvs/my_website_env
Configure WSGI file (click on WSGI config file link in Web tab):
import os
import sys
# Replace YOUR_USERNAME and my_website with your values
path = '/home/YOUR_USERNAME/my_website'
if path not in sys.path:
sys.path.append(path)
# Replace 'my_website' with your Django project name
os.environ['DJANGO_SETTINGS_MODULE'] = 'my_website.settings'
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
- Reload web app - Click green "Reload" button
Your site should now be live at: YOUR_USERNAME.pythonanywhere.com
Step 5: Setup Custom Domain (Optional)
Note: Requires PythonAnywhere Hacker account ($5/month)
-
In PythonAnywhere Web tab:
- Add custom domain:
www.yourdomain.com(replace with your domain) - Note the CNAME value:
webapp-XXXXXX.pythonanywhere.com
- Add custom domain:
-
In your domain registrar (Namecheap, GoDaddy, Joker.com, etc):
- Add CNAME record:
wwwβwebapp-XXXXXX.pythonanywhere.com - Optionally add A record for root domain (check PythonAnywhere docs)
- Add CNAME record:
-
Enable HTTPS:
- In PythonAnywhere Web tab β HTTPS section
- Click "Enable HTTPS" or "Renew certificate"
Wait 10-60 minutes for DNS propagation.
If you're not using a custom domain: Skip this step and use YOUR_USERNAME.pythonanywhere.com
Step 6: Setup Automated Deployment
This is where the magic happens - automatic deployment on every Git push!
6.1 Create GitHub Personal Access Token
- GitHub β Settings β Developer settings β Personal access tokens β Tokens (classic)
- Generate new token (classic)
- Select scope: βοΈ repo (all checkboxes under repo)
- Copy the token (starts with
ghp_...)
6.2 Configure Git Authentication on PythonAnywhere
cd ~/my_website
git remote set-url origin https://github.com/YOUR_USERNAME/my_website.git
git config credential.helper store
git pull origin main
# Username: YOUR_GITHUB_USERNAME
# Password: PASTE_YOUR_TOKEN_HERE
The token is now saved and git pull will work automatically.
6.3 Create GitHub Actions Workflow
Create .github/workflows/deploy.yml in your project:
name: Deploy to PythonAnywhere
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1.0.0
with:
host: ssh.pythonanywhere.com
username: ${{ secrets.PYTHONANYWHERE_USERNAME }}
password: ${{ secrets.PYTHONANYWHERE_PASSWORD }}
script: |
cd ~/my_website
git pull origin main
touch /var/www/YOUR_USERNAME_pythonanywhere_com_wsgi.py
- name: Reload web app via API
run: |
curl -X POST \
https://www.pythonanywhere.com/api/v0/user/${{ secrets.PYTHONANYWHERE_USERNAME }}/webapps/YOUR_USERNAME.pythonanywhere.com/reload/ \
-H "Authorization: Token ${{ secrets.PYTHONANYWHERE_API_TOKEN }}"
Important replacements in the workflow:
- Line 17:
cd ~/my_websiteβ Use your project directory name - Line 19:
YOUR_USERNAME_pythonanywhere_com_wsgi.pyβ Find exact filename in PythonAnywhere/var/www/directory - Line 24:
YOUR_USERNAME.pythonanywhere.comβ Use your actual PythonAnywhere URL (or custom domain if configured)
6.4 Add GitHub Secrets
Go to: Repository β Settings β Secrets and variables β Actions
Create three secrets:
-
PYTHONANYWHERE_USERNAME- Value: Your PythonAnywhere username
-
PYTHONANYWHERE_PASSWORD- Value: Your PythonAnywhere password
-
PYTHONANYWHERE_API_TOKEN- Get from: PythonAnywhere β Account β API token β Create new
- Value: The generated token
Step 7: Test Automated Deployment
# Make a small change in your code
# For example, edit index.html or a template file
# Commit and push
git add .
git commit -m "Test automated deployment"
git push origin main
What happens now:
- GitHub receives your push
- GitHub Actions workflow triggers
- Connects to PythonAnywhere via SSH
- Runs
git pullto get latest code - Reloads web app via API
- Your site is updated! (takes ~1-2 minutes)
Check: GitHub β Actions tab to see workflow running
β Final Workflow
Your development workflow is now:
1. Edit code in VS Code
2. Save changes
3. git add . && git commit -m "message" && git push origin main
4. Wait 1-2 minutes
5. Site is live! π
Check your live site at:
-
PythonAnywhere subdomain:
YOUR_USERNAME.pythonanywhere.com -
Custom domain (if configured):
www.yourdomain.com
π Common Issues & Solutions
Issue 1: "DisallowedHost" error
Solution: Add your domain to ALLOWED_HOSTS in settings.py:
ALLOWED_HOSTS = [
'yourdomain.com',
'www.yourdomain.com',
'YOUR_USERNAME.pythonanywhere.com'
]
Issue 2: "Permission denied (publickey)" when git pull
Solution: Change Git remote to HTTPS and use Personal Access Token:
git remote set-url origin https://github.com/YOUR_USERNAME/your_project.git
git config credential.helper store
git pull # Enter username and token
Issue 3: GitHub Actions workflow fails
Solution:
- Check that all three Secrets are correctly set in GitHub
- Verify PythonAnywhere API token is valid
- Check workflow logs in GitHub Actions tab
-
Important: Verify WSGI file path and website URL in
deploy.ymlmatch your actual setup
Issue 4: Site doesn't update after push
Solution:
- Check GitHub Actions - did workflow succeed?
- SSH into PythonAnywhere and manually run
git pull - Check error logs in PythonAnywhere Web tab
- Verify the
touchcommand uses correct WSGI file path
Issue 5: 502 Bad Gateway error
Solution:
- Check WSGI configuration file has correct project name
- Verify virtual environment is activated and has dependencies
- Check error logs in PythonAnywhere
π Project Architecture
βββββββββββββββ
β VS Code β (Local Development)
ββββββββ¬βββββββ
β git push
β
βββββββββββββββ
β GitHub β (Version Control)
ββββββββ¬βββββββ
β triggers
β
βββββββββββββββββββ
β GitHub Actions β (CI/CD Pipeline)
ββββββββ¬βββββββββββ
β SSH + API
β
ββββββββββββββββββββ
β PythonAnywhere β (Production Server)
ββββββββ¬ββββββββββββ
β serves
β
ββββββββββββββββββββ
β Your Website β (Live Site)
ββββββββββββββββββββ
π Key Learnings
-
Django configuration matters -
import osandALLOWED_HOSTSare critical - GitHub Actions is powerful - Automate everything!
- Personal Access Tokens - Replace password authentication for Git
- WSGI configuration - Connects Django to web server
- Systematic debugging - Check each layer: local β Git β GitHub β server β live site
You now have a Django website with automated deployment. Every time you push code, your site updates automatically. No manual deployment needed!
Tutorial by Teemu Virta - teemu.tech
Top comments (0)