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
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:
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:
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:
- 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:
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
concurrencygroups 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
Tagged with
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.
Written by
Priya Kapoor
Tech enthusiast and creative writer sharing insights on modern development.
View all postsDiscussion
0 comments
Sign in to join the conversation.
Be the first to comment
Start a conversation about this post
