
Building a CI/CD Pipeline with GitHub Actions: Node.js Example
Manual testing and deployment processes are both slow and error-prone. GitHub Actions is a free CI/CD platform that automatically runs test, build, and deploy steps when code is pushed. This guide builds a complete pipeline for a Node.js application including testing, Docker image building, security
Elif Demir
Cloud Solutions Architect
Manual testing and deployment processes are both slow and error-prone. GitHub Actions is a free CI/CD platform that automatically runs test, build, and deploy steps when code is pushed. This guide builds a complete pipeline for a Node.js application including testing, Docker image building, security scanning, and Kubernetes deployment.
GitHub Actions Basics
GitHub Actions are defined by YAML files in the .github/workflows/ directory of your repository. Each workflow contains one or more jobs, and each job consists of steps. Workflows can be triggered by push, pull request, schedule, or manual dispatch.
| Concept | Description | Example |
|---|---|---|
| Workflow | Automated process definition | ci.yml file |
| Job | Work unit running in parallel or sequence | test, build, deploy |
| Step | A single command or action within a job | npm test, docker build |
| Action | Reusable step package | actions/checkout@v4 |
Test and Lint Pipeline
As a first step, let's create a workflow that automatically runs tests and linting on every push and pull request.
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm test -- --coverage
- uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.node-version }}
path: coverage/
Docker Build and Deploy Pipeline
After tests pass, here is the full pipeline that builds a Docker image, pushes it to a registry, and deploys to Kubernetes:
name: Build & Deploy
on:
push:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: azure/k8s-set-context@v4
with:
kubeconfig: ${{ secrets.KUBE_CONFIG }}
- run: |
kubectl set image deployment/web-app \
web-app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
-n production
💡 Tip: cache-from: type=gha uses GitHub Actions cache to significantly reduce Docker build times. After the first build, unchanged layers are served from cache.
Pipeline Security Steps
-
Store Secrets in Repository Settings Store API keys, kubeconfig, and registry credentials in GitHub Secrets. Never write them as plain text in workflow files.
-
Pin Action Versions Use
actions/[email protected]or commit SHA instead ofactions/checkout@v4. This protects against supply chain attacks. -
Add Image Security Scanning Scan images with Trivy after build. Stop the pipeline if critical vulnerabilities are found:
trivy image --exit-code 1 --severity CRITICAL -
Apply the Principle of Least Privilege Use the
permissionsblock to grant each job only the permissions it needs. By default, all permissions are open.
For Docker image optimization, check our Multi-Stage Build guide. For container security, see our Container Security guide. For a GitOps approach, explore our ArgoCD guide. The GitHub Actions documentation and Actions Marketplace are valuable additional resources.
Frequently Asked Questions
Is GitHub Actions free?
It is completely free for public repositories. Private repositories get 2,000 free minutes per month on the Free plan. Additional minutes are charged. You can reduce costs to zero by using self-hosted runners.
What is the difference between GitHub Actions and Jenkins?
GitHub Actions is cloud-based and natively integrated with GitHub, requiring no server management. Jenkins is self-hosted, more flexible but requires setup and maintenance. GitHub Actions suits small-to-medium teams, while Jenkins may be better for complex enterprise pipelines.
What does matrix strategy do?
Matrix strategy runs the same job in parallel with different parameters. For example, you can test simultaneously on Node.js 18, 20, and 22. This helps you catch compatibility issues early.
When should I use a self-hosted runner?
Use self-hosted runners when you need special hardware (GPU, ARM), network restrictions (private network access), or cost optimization. For security, run your runners in an isolated environment and update them regularly.
Conclusion
By automating test, build, and deploy processes with GitHub Actions, you can increase development speed and reduce error rates. Use matrix strategy for multi-version testing, Docker cache to shorten build times, security scanning to catch vulnerabilities early, and proper secret management to keep credentials safe.
Powerful Infrastructure for Your CI/CD Pipeline
Deploy your applications fast and securely with Hosted Cloud servers.
Explore Cloud Server Plans →Elif Demir
Cloud Solutions Architect
Specializing in enterprise cloud migration projects and hybrid infrastructure design with 8 years of experience in AWS, Azure, and private cloud environments.
Comments coming soon