CI/CD with GitHub Actions

June 13, 2026

Before working with CI/CD on GitHub, I have added my own private runner to run my private CI/CD pipelines.

Adding a self-hosted runner to GitHub is pretty straightforward.

Go to your GitHub repository, click on "Settings" > "Actions" > "Runners" > "New self-hosted runner".

Create a new user on your Virtual Machine. Because it is not allowed to run self-hosted runner as root user on your machine.

sudo adduser github-runner

Switch to the github-runner user on your VM and follow the instructions provided by GitHub to download and configure the runner on your VM. This typically involves running a few commands in the terminal to set up the runner and connect it to your GitHub repository.

To use the runner on Github actions, we need to install the runner as a service. Otherwise, the runner goes offline.

We switch to root user to install the runner service. ./svc.sh can be found inside the directory you used to configure from the Github documentation.

sudo ./svc.sh install
sudo ./svc.sh start

To run the job I use Docker runners. This has few benefits

  • Isolation: Each job runs in its own container. No dependency on host Python
  • Consistency: The same Docker image can be used across different environments
  • Maintainability: Easier upgrades and easier to manage dependencies

Install Docker from the Official Docker documentation: https://docs.docker.com/engine/install/ubuntu/

Give the github-runner user permission to run Docker commands:

sudo usermod -aG docker github-runner 

Now add a Lint and Test workflow to your GitHub repository. Create a new file in your repository at .github/workflows/lint.yml with the following content. This workflow will run on every push, and pull request to the repository, and it will use your self-hosted runner to execute the jobs using the specified Docker image python:3.12.

name: CI
on: [ push, pull_request ]
jobs:
  lint:
    runs-on: self-hosted
    strategy:
      matrix:
        python-version: [ "3.12" ]
    container:
      image: python@sha256:dbb1970cc04ce7d381c65efe8309c0c03d463e5b35c88f14d721796ad24cfbfd
    steps:
      - uses: actions/checkout@v7
      - name: Install dependencies
        run: |
          python -m venv venv
          source venv/bin/activate
          pip install --upgrade pip
          pip install ruff==0.15.0
      - name: Analysing the code with pylint
        run: |
          source venv/bin/activate
          ruff check .
  test:
    runs-on: self-hosted
    container:
      image: python@sha256:dbb1970cc04ce7d381c65efe8309c0c03d463e5b35c88f14d721796ad24cfbfd
    steps:
      - uses: actions/checkout@v7
      - name: install dependencies
        run: |
          python -m venv venv
          source venv/bin/activate
          pip install --upgrade pip
          pip install -r requirements.txt
      - name: Run tests
        run: |
          source venv/bin/activate
          python manage.py test --settings=myapp.test_settings