Back to articles
May 21, 2026

Building a CI/CD Pipeline from Scratch with GitHub Actions

What is CI/CD? Continuous Integration (CI) and Continuous Deployment (CD) are practices that automate the process of building, testing, and releasing software. CI ensures that every code change is…

the best way to build web apps without codePhoto: Team Nocoloco / Unsplash

What is CI/CD?

Continuous Integration (CI) and Continuous Deployment (CD) are practices that automate the process of building, testing, and releasing software. CI ensures that every code change is built and tested automatically. CD takes it further by deploying those changes to staging or production environments without manual intervention.

Together, they reduce release risk, catch bugs early, and free developers from repetitive manual tasks.

Why GitHub Actions?

GitHub Actions is a built-in CI/CD platform available on GitHub repositories. It eliminates the need for external tools like Jenkins or CircleCI for many use cases. Workflows are defined in YAML files stored directly in your repository, making them version-controlled, reviewable, and easy to share.

Setting Up Your First Workflow

Create a file at .github/workflows/ci.yml in your repository:

name: CI Pipeline

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

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Set up 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
      env:
        CI: true

    - name: Build application
      run: npm run build

Adding a Deployment Job

Extend the pipeline to deploy to a staging environment after tests pass:

  deploy-staging:
    needs: build-and-test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Deploy to staging
      run: |
        echo "Deploying to staging environment..."
        ./scripts/deploy.sh staging
      env:
        DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
        STAGING_URL: ${{ secrets.STAGING_URL }}

Environment-Specific Variables

GitHub Actions supports environment-scoped secrets and variables. Define them under Settings > Environments in your repository:

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest

    environment:
      name: production
      url: https://myapp.com

    steps:
    - name: Deploy to production
      run: ./scripts/deploy.sh production
      env:
        DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

The environment key gates the job behind manual approval, giving your team a safety checkpoint before production deployments.

Caching and Performance Tips

Speed matters in CI. A slow pipeline discourages developers from using it.

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

Cache build artifacts, Docker layers, and dependency folders to cut pipeline times significantly.

Conclusion

A well-designed CI/CD pipeline is one of the highest-ROI investments a team can make. Start with a simple build-and-test workflow, add deployment stages as needed, and continuously refine based on feedback. GitHub Actions makes it easy to iterate — your pipeline evolves alongside your application.