Tutorials November 16, 2025 3 mins read

CI/CD Snippets: GitHub Actions for Node, PHP, Python & Docker

This page gives you clean, production-ready CI/CD workflows you can paste directly into your repository. Each workflow is minimal, fast, and built around real-world usage: Node builds, PHP tests (Pest/PHPUnit), Python testing, Docker build/push, matrix strategies, and caching.

This page gives you clean, production-ready CI/CD workflows you can paste directly into your repository. Each workflow is minimal, fast, and built around real-world usage: Node builds, PHP tests (Pest/PHPUnit), Python testing, Docker build/push, matrix strategies, and caching.

Use these as boilerplates or as reference setups when building your own automation pipelines.

Node.js (build/test)

name: Node CI

on:
  push:
    branches: [ main ]
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - run: npm ci
      - run: npm run build --if-present
      - run: npm test

PHP (Pest/PHPUnit) — Composer + Extensions

name: PHP CI

on:
  push:
    branches: [ main ]
  pull_request:

jobs:
  tests:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          extensions: mbstring, intl, pdo_mysql
          coverage: none

      - name: Cache Composer
        uses: actions/cache@v4
        with:
          path: vendor
          key: composer-${{ hashFiles('composer.lock') }}

      - name: Install dependencies
        run: composer install --prefer-dist --no-progress

      - name: Run tests
        run: ./vendor/bin/pest || ./vendor/bin/phpunit

Python (pytest)

name: Python CI

on:
  push:
    branches: [ main ]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'

      - run: pip install -r requirements.txt
      - run: pytest -q

Docker Build & Push (GHCR)

name: Docker Build & Push

on:
  push:
    branches: [ main ]

jobs:
  docker:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build & push
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: |
            ghcr.io/${{ github.repository }}/app:latest
            ghcr.io/${{ github.repository }}/app:${{ github.sha }}

Matrix Builds (Multi-Version Node Example)

name: Node Matrix

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node: [18, 20, 22]

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
          cache: 'npm'

      - run: npm ci
      - run: npm test

Cache Snippets (npm / composer / pip)

npm cache:

- uses: actions/cache@v4
  with:
    path: node_modules
    key: npm-${{ hashFiles('package-lock.json') }}

composer cache:

- uses: actions/cache@v4
  with:
    path: vendor
    key: composer-${{ hashFiles('composer.lock') }}

pip cache:

- uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: pip-${{ hashFiles('requirements.txt') }}

FAQ

Self-hosted vs GitHub-hosted runners?

GitHub-hosted runners are fast, reliable, and ideal for 95% of repositories.
Self-hosted makes sense only for heavy builds, private infrastructure, or large workflow volumes.

How to cache npm/pip/composer properly?

Use actions/cache with lockfile hashing — this ensures cache invalidation when dependencies change.

Secrets management best practices?

Never hardcode secrets in YAML.
Use GitHub Secrets + environment-level protections (required reviewers, restricted deployments).

David Green

David Green