What You'll Learn
How to build CI/CD pipelines with GitHub Actions — workflows, jobs, steps, secrets management, and a complete production example for a Dockerized application.
What is GitHub Actions?
GitHub Actions is a CI/CD platform deeply integrated into GitHub. Instead of running a separate CI server (like Jenkins), your code repository itself executes workflows in response to GitHub events (push, PR, release).
The entire automated process (YAML file)
Run in parallel on different runners
Run sequentially (commands or actions)
Complete Production Workflow
Workflows live in the .github/workflows/ directory. Here is a complete example of a Node.js application that runs tests on PRs, and builds/deploys a Docker image on merges to main.
name: Production CI/CD
# 1. TRGGERS: When should this run?
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch: # Allows manual triggering from GitHub UI
# Global environment variables
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# ─── JOB 1: Test & Lint (Runs on PR and Push) ───
test:
name: Run Tests & Lint
runs-on: ubuntu-latest
# Run a Redis container for the tests
services:
redis:
image: redis:alpine
ports: [ "6379:6379" ]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # Speeds up builds by caching node_modules!
- name: Install dependencies
run: npm ci
- name: Run Linter
run: npm run lint
- name: Run Unit Tests
run: npm run test:coverage
env:
REDIS_URL: redis://localhost:6379
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/
retention-days: 7
# ─── JOB 2: Build & Push Docker Image (Only on main) ───
build-and-push:
name: Build & Push Docker Image
needs: test # Waits for test job to pass!
if: github.ref == 'refs/heads/main' # Only run on main branch
runs-on: ubuntu-latest
# Needs permissions to write to GitHub Container Registry
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} # Auto-provided by GitHub
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,format=short
type=ref,event=branch
latest
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# ─── JOB 3: Deploy to Kubernetes ───
deploy:
name: Deploy to Production
needs: build-and-push
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
# Require manual approval (Environment Protection Rules)
environment: production
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set target image tag
run: echo "IMAGE_TAG=sha-${GITHUB_SHA::7}" >> $GITHUB_ENV
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Update KubeConfig
run: aws eks update-kubeconfig --name prod-cluster --region us-east-1
- name: Deploy to EKS
run: |
kubectl set image deployment/api-server app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}
kubectl rollout status deployment/api-server --timeout=5m
GitHub Actions Best Practices
- Pin action versions: Always use `@v4` or full commit SHAs, never `@master`.
- Use Caching: Use `actions/cache` or `cache: 'npm'` to cache dependencies and speed up workflows.
- Use Environments: Configure 'Environments' in repo settings to require manual approvals before deployment jobs run.
- OIDC for Cloud Auth: Instead of storing long-lived AWS keys, use OpenID Connect (OIDC) to assume roles securely.
- Matrix builds: Test across multiple Node/Python versions simultaneously using `strategy: matrix`.