Resourcesโ€บDevOps Hacksโ€บGitHub Actions: 7 Tricks to Speed Up Your CI/CD Pipeline
๐ŸšขDevOps Hacksโ€” GitHub Actions: 7 Tricks to Speed Up Your CI/CD Pipelineโฑ 7 min

GitHub Actions: 7 Tricks to Speed Up Your CI/CD Pipeline

Slow CI kills developer velocity. These 7 GitHub Actions tricks cut build times, eliminate flakiness, and make your pipeline reliable.

๐Ÿ“…February 3, 2026โœTechTwitter.iogithub-actionsci-cddevopsautomation

Why CI Speed Matters

A 20-minute CI pipeline that runs 50 times a day is 16+ hours of developer waiting. When CI is fast, developers get feedback quickly, push smaller commits, and iterate faster.

These tricks have consistently delivered 50-80% CI time reductions.


1. Cache Dependencies

The single biggest win in most pipelines. Node modules, pip packages, cargo dependencies โ€” cache them between runs:

- name: Cache node modules
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

- name: Install dependencies
  run: npm ci

The hashFiles key means the cache is only invalidated when package-lock.json changes. Cache hits skip the install entirely.


2. Parallelize Jobs

Don't run tests after lint after type-check sequentially. Run them in parallel:

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run lint

  typecheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npx tsc --noEmit

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm test

  build:
    needs: [lint, typecheck, test]  # only runs if all pass
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build

Wall time = longest job, not sum of all jobs.


3. Use actions/setup-node with Caching Built In

Many setup actions now have built-in caching:

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

Simpler than managing the cache action manually.


4. Skip Unchanged Code with Path Filters

Don't run your whole test suite when only docs changed:

on:
  push:
    paths-ignore:
      - '**.md'
      - 'docs/**'

# Or only run specific jobs based on what changed:
jobs:
  changes:
    runs-on: ubuntu-latest
    outputs:
      api: ${{ steps.filter.outputs.api }}
      frontend: ${{ steps.filter.outputs.frontend }}
    steps:
      - uses: dorny/paths-filter@v3
        id: filter
        with:
          filters: |
            api:
              - 'src/api/**'
            frontend:
              - 'src/frontend/**'

  test-api:
    needs: changes
    if: needs.changes.outputs.api == 'true'
    runs-on: ubuntu-latest
    steps:
      - run: npm test -- --testPathPattern=api

5. Use Reusable Workflows

Stop copy-pasting the same CI steps across repositories:

# .github/workflows/shared-test.yml
on:
  workflow_call:
    inputs:
      node-version:
        required: false
        type: string
        default: '20'

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
          cache: 'npm'
      - run: npm ci && npm test
# In any other repo:
jobs:
  test:
    uses: myorg/shared-workflows/.github/workflows/shared-test.yml@main
    with:
      node-version: '20'

6. Fail Fast, Cancel Redundant Runs

Cancel in-progress CI when a new commit is pushed to the same PR:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

Add this at the top level of your workflow. No more 5 queued CI runs for a branch you're actively working on.


7. Use GitHub Actions Cache for Docker Layers

Building Docker images without layer caching is painfully slow:

- name: Set up Docker Buildx
  uses: docker/setup-buildx-action@v3

- name: Build and push
  uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: myapp:${{ github.sha }}
    cache-from: type=gha        # Use GitHub Actions cache
    cache-to: type=gha,mode=max # Write to GitHub Actions cache

The type=gha cache persists Docker layer cache between workflow runs, dramatically speeding up image builds when only source code changes.


Bonus: Monitor Your CI Times

- name: Report timing
  if: always()
  run: |
    echo "Job duration: ${{ job.duration }}s" >> $GITHUB_STEP_SUMMARY

Track your CI times over time. If a job grows from 3 minutes to 15 minutes, you want to know when and why.


Key Takeaways

  • Cache dependencies โ€” the single biggest speedup in most pipelines
  • Parallelize jobs โ€” wall time = longest job, not sum of all
  • Path filters โ€” skip unchanged code to skip unnecessary work
  • Concurrency + cancel โ€” eliminate queued runs from rapid commits
  • Docker layer cache โ€” use type=gha for build-push-action
  • Reusable workflows โ€” share CI logic across repositories without copy-paste