DevOps

Automating CI/CD with GitHub Actions

Introduction

Continuous Integration and Continuous Deployment (CI/CD) are foundational practices in modern software engineering. They allow teams to catch bugs early, automate tedious release steps, and ship code with confidence. In this guide I'll walk you through setting up a production-ready CI/CD pipeline for a Python/Django project using GitHub Actions and Docker.

Step 1 โ€” Set Up Your GitHub Repository

Start by creating a GitHub repository for your Django project. Make sure your project has a requirements.txt and a working test suite before continuing. Good CI/CD starts with a solid test baseline.

Step 2 โ€” Create the GitHub Actions Workflow

Create the file .github/workflows/main.yml at the root of your repository. This YAML file defines your entire pipeline:

name: CI/CD Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: pip install -r requirements.txt
      - run: python manage.py test

  build-and-push:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build Docker image
        run: docker build -t myapp:${{ github.sha }} .
      - name: Push to Docker Hub
        run: |
          echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
          docker push myapp:${{ github.sha }}

Step 3 โ€” Configure Docker

Create a Dockerfile in your project root. A good practice is to use a multi-stage build to keep your production image lean:

FROM python:3.11-slim AS base
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]

Step 4 โ€” Auto-Deploy on Push

Add a deploy job to your workflow that SSHes into your server (or uses a Kubernetes rolling update) to pull the new Docker image and restart the service. Store all credentials as GitHub Secrets โ€” never hard-code them in the YAML file.

Conclusion

With this pipeline in place, every push to main automatically runs your tests, builds a Docker image, and deploys to production. This dramatically reduces manual release risk and accelerates your delivery cadence.

← Back to Articles Next: Kubernetes Deployment →