Django CI/CD Pipeline with GitHub Actions
Django CI/CD Pipeline with GitHub Actions
Manually deploying your Django project every time you make a change slows down your workflow and increases the risk of mistakes.
A CI/CD pipeline (Continuous Integration and Continuous Deployment) ensures that your code is tested, built, and deployed automatically every time you push to your main branch.
This tutorial shows how to build a full pipeline for your Django app using GitHub Actions and Fly.io.
๐ What Youโll Learn
- Automate testing and migrations
- Build Docker images for Django
- Deploy automatically to Fly.io
- Use GitHub Secrets to store environment variables
๐งฉ Prerequisites
Before we begin, make sure you have:
- A Django app (preferably containerized with Docker)
- A Fly.io account and working app (
fly.tomlalready created) - GitHub repo connected to your project
๐ณ Step 1: Dockerize Your Django App
If you donโt already have a Dockerfile, hereโs a minimal example:
# Dockerfile
FROM python:3.11-slim
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["gunicorn", "projectname.wsgi:application", "--bind", "0.0.0.0:8000"]
and .dockerignore:
__pycache__
*.pyc
.env
.git
Build and test locally:
docker build -t myapp .
docker run -p 8000:8000 myapp
โ๏ธ Step 2: Configure Fly.io for Deploys
Initialize your app if you havenโt yet:
fly launch
This creates a fly.toml configuration file that looks like this:
app = "your-app-name"
[build]
image = "registry.fly.io/your-app-name:latest"
[env]
DJANGO_SETTINGS_MODULE = "projectname.settings"
PORT = "8000"
๐ Step 3: Add Secrets to Fly.io
Store production secrets on Fly.io (so theyโre not in your repo):
fly secrets set DJANGO_SECRET_KEY='supersecret'
fly secrets set DEBUG='False'
fly secrets set DATABASE_URL='postgresql://user:password@host:5432/dbname'
๐ง Step 4: Add GitHub Secrets
In your GitHub repo:
- Go to Settings โ Secrets and variables โ Actions
- Add the following secrets:
| Name | Description |
|---|---|
FLY_API_TOKEN | Your Fly.io API token (fly auth token) |
DJANGO_SECRET_KEY | Djangoโs secret key |
DATABASE_URL | Connection string to production DB |
DEBUG | Set to False |
โก Step 5: Create the GitHub Actions Workflow
Create a new workflow file:
.github/workflows/deploy.yml
name: Deploy Django to Fly.io
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 black
pip install -r requirements.txt
- name: Run code quality checks
run: |
black --check .
flake8 .
- name: Run Django tests
run: |
python manage.py collectstatic --noinput
python manage.py test
- name: Set up Fly.io
uses: superfly/flyctl-actions/setup-flyctl@master
- name: Deploy to Fly.io
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
run: |
flyctl deploy --remote-only
๐งช Step 6: Trigger the Workflow
Push a commit to your main branch:
git add .
git commit -m "Enable GitHub Actions deploy"
git push origin main
Go to your GitHub repo โ Actions tab โ watch the deployment run.
Once complete, Fly.io will automatically build and deploy your app. ๐
๐งฐ Step 7: Add Automatic Database Migrations
You can chain migrations to run right after deploy.
Add this job after deployment in deploy.yml:
run-migrations:
needs: deploy
runs-on: ubuntu-latest
steps:
- name: Set up Fly.io
uses: superfly/flyctl-actions/setup-flyctl@master
- name: Run Django migrations
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
run: |
flyctl ssh console -C "python manage.py migrate"
Now, every deploy will:
- Test your code
- Build your Docker image
- Push to Fly.io
- Run migrations automatically
๐ Step 8: Optional โ Staging Environment
If you want a staging app:
fly apps create myapp-staging
fly launch --name myapp-staging
Then duplicate your workflow file and deploy from a staging branch instead of main.
on:
push:
branches: [staging]
You can even use separate secrets (FLY_API_TOKEN_STAGING, etc.).
๐งญ Step 9: CI Enhancements
Add more checks as your app grows:
- pytest-django for unit tests
- Bandit for security scans
- Black + Flake8 for linting
- isort for import ordering
Example:
pip install pytest pytest-django bandit
pytest
bandit -r .
๐ Step 10: Monitor and Roll Back
Fly.io keeps image versions automatically.
To rollback to a previous version:
fly releases
fly deploy --image <previous_image>
You can also view logs in real time:
fly logs
๐ Conclusion
You now have a production-ready Django CI/CD pipeline:
โ
Automated testing and code checks
โ
Zero-downtime Docker deploys
โ
Environment secrets stored securely
โ
Optional staging + rollback
With this workflow, your Fly.io apps deploy themselves โ every push ships production code.
Written by Bailey Burnsed โ Senior Software Engineer, Founder of BaileyBurnsed.dev