Skip to main content
CI/CDBeginner

Setting Up a CI/CD Pipeline with GitHub Actions

Build a complete CI/CD pipeline with GitHub Actions — from running tests on every PR to automated production deployments.

P
Priya Kapoor
·Updated September 4, 2026·9 min read·147 views
Setting Up a CI/CD Pipeline with GitHub Actions

Setting Up a CI/CD Pipeline with GitHub Actions

GitHub Actions lets you automate your build, test, and deployment workflows directly from your repository. It's free for public repos and has generous free-tier minutes for private ones.

How GitHub Actions Works

Workflows are defined in YAML files inside .github/workflows/. They are triggered by events like pushes, pull requests, or schedules.

Key concepts:

  • Workflow — an automated process defined in YAML
  • Job — a set of steps that run on the same runner
  • Step — an individual task (run a command or use an action)
  • Runner — the VM that executes your job

Basic CI Workflow

yaml
name: CI

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

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Run linter
        run: npm run lint

      - name: Run tests
        run: npm test

      - name: Build
        run: npm run build

Adding a Database Service

Many apps need a database for integration tests:

yaml
jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_DB: testdb
          POSTGRES_PASSWORD: testpass
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    env:
      DATABASE_URL: postgresql://postgres:testpass@localhost:5432/testdb
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npx prisma migrate deploy
      - run: npm test

Deploying to Production

Add a deployment job that runs only after tests pass:

yaml
  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    steps:
      - uses: actions/checkout@v4

      - name: Deploy to production
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
        run: |
          echo "Deploying to production..."
          # Your deployment commands here

Caching Dependencies

Speed up workflows with caching:

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

Matrix Builds

Test across multiple versions:

yaml
    strategy:
      matrix:
        node-version: [18, 20, 22]
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}

Reusable Workflows and Secrets

  • Store sensitive values in Settings → Secrets and variables → Actions
  • Reference them as ${{ secrets.MY_SECRET }}
  • Use reusable workflows (workflow_call) to DRY up common patterns across repos

Tips

  • Use concurrency groups to cancel redundant runs
  • Pin action versions to a specific commit SHA for security
  • Enable branch protection rules requiring CI to pass before merge
  • Keep workflows under 60 minutes; split long ones into parallel jobs

Enjoyed this article?

Get more DevOps insights delivered to your inbox.

Get new posts by email

Subscribe to get an email when a new blog post is published. Skip anytime.

No spam, unsubscribe anytime.

P

Written by

Priya Kapoor

Tech enthusiast and creative writer sharing insights on modern development.

View all posts

Discussion

0 comments

Sign in to join the conversation.

Be the first to comment

Start a conversation about this post

Share: