Back to Blog
Developer ToolsCI/CDGitHubDevOpsTutorial

Set Up GitHub Actions CI/CD -- A Hands-On Guide for Any Project

Published on April 4, 202614 min read

Set Up GitHub Actions CI/CD -- A Hands-On Guide for Any Project

Every time you push code, you should not have to manually run tests, build the project, check formatting, and deploy. That is what CI/CD is for -- it automates the boring, repetitive parts of shipping software so you can focus on writing code.

GitHub Actions is built into every GitHub repository for free. This guide walks you through setting up real CI/CD pipelines, not toy examples.


How GitHub Actions Works

GitHub Actions uses YAML files in the .github/workflows/ directory of your repository. Each file defines a workflow -- a set of jobs that run in response to events like pushes, pull requests, or schedules.

Key concepts:

  • Workflow: A YAML file that defines the automation. You can have multiple workflows per repository.
  • Event: What triggers the workflow (push, pull_request, schedule, manual dispatch).
  • Job: A set of steps that run on the same machine. Jobs run in parallel by default.
  • Step: A single task within a job. Can be a shell command or a pre-built action.
  • Action: A reusable unit of code. The marketplace has thousands of them.
  • Runner: The machine that runs your job. GitHub provides free Linux, macOS, and Windows runners.

Your First Workflow

Create the directory and file:

mkdir -p .github/workflows

Create .github/workflows/ci.yml:

name: CI

>

on:

push:

branches: [main]

pull_request:

branches: [main]

>

jobs:

test:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- uses: actions/setup-node@v4

with:

node-version: 20

- run: npm ci

- run: npm test

Push this file and go to the Actions tab in your GitHub repository. You will see the workflow running.

What This Does

  1. 1Triggers on every push to main and every PR targeting main
  2. 2Checks out your code
  3. 3Sets up Node.js 20
  4. 4Installs dependencies with npm ci (clean install)
  5. 5Runs your test suite

Real-World CI Pipeline for Node.js

Here is a production-ready CI pipeline that does more than just run tests:

name: CI

>

on:

push:

branches: [main]

pull_request:

branches: [main]

>

jobs:

lint:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- uses: actions/setup-node@v4

with:

node-version: 20

cache: "npm"

- run: npm ci

- run: npm run lint

>

typecheck:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- uses: actions/setup-node@v4

with:

node-version: 20

cache: "npm"

- run: npm ci

- run: npx tsc --noEmit

>

test:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- uses: actions/setup-node@v4

with:

node-version: 20

cache: "npm"

- run: npm ci

- run: npm test -- --coverage

- uses: actions/upload-artifact@v4

with:

name: coverage-report

path: coverage/

>

build:

needs: [lint, typecheck, test]

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- uses: actions/setup-node@v4

with:

node-version: 20

cache: "npm"

- run: npm ci

- run: npm run build

What Makes This Better

  • Parallel jobs: Lint, typecheck, and test run simultaneously, cutting total pipeline time
  • Dependency caching: The cache: "npm" line caches node_modules between runs, making installs near-instant
  • Build depends on quality gates: The build job only runs if lint, typecheck, and test all pass
  • Coverage artifact: Test coverage is uploaded and available for download from the workflow run

CI Pipeline for Python

name: CI

>

on:

push:

branches: [main]

pull_request:

branches: [main]

>

jobs:

test:

runs-on: ubuntu-latest

strategy:

matrix:

python-version: ["3.10", "3.11", "3.12"]

steps:

- uses: actions/checkout@v4

- uses: actions/setup-python@v5

with:

python-version: python-version-from-matrix

cache: "pip"

- run: pip install -r requirements.txt

- run: pip install pytest pytest-cov

- run: pytest --cov=src tests/

>

lint:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- uses: actions/setup-python@v5

with:

python-version: "3.12"

- run: pip install ruff

- run: ruff check .

- run: ruff format --check .

The matrix strategy runs tests across multiple Python versions simultaneously.


Docker Build and Push

For projects that deploy as Docker containers:

name: Build and Push Docker Image

>

on:

push:

branches: [main]

>

jobs:

docker:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

>

- uses: docker/setup-buildx-action@v3

>

- uses: docker/login-action@v3

with:

registry: ghcr.io

username: github-actor

password: secrets-github-token

>

- uses: docker/build-push-action@v5

with:

context: .

push: true

tags: ghcr.io/your-username/your-app:latest

cache-from: type=gha

cache-to: type=gha,mode=max

This builds your Docker image and pushes it to GitHub Container Registry (ghcr.io). The cache-from and cache-to lines use GitHub Actions cache for Docker layers, making subsequent builds much faster.


Managing Secrets

Never put API keys, tokens, or passwords in your workflow files. Use GitHub Secrets:

  1. 1Go to your repository Settings -> Secrets and variables -> Actions
  2. 2Click "New repository secret"
  3. 3Add your secret (e.g., DEPLOY_TOKEN)

Use it in your workflow:

- run: deploy --token my-token

env:

DEPLOY_TOKEN: secrets-deploy-token

Secrets are masked in logs -- if they accidentally appear in output, GitHub redacts them automatically.

Environment Secrets

For different secrets per environment (staging vs production):

  1. 1Create environments in Settings -> Environments
  2. 2Add secrets to each environment
  3. 3Reference the environment in your job:

> jobs:

> deploy:

> runs-on: ubuntu-latest

> environment: production

> steps:

> - run: deploy --token my-production-token


Deployment Workflows

Deploy to Vercel

> name: Deploy

>

> on:

> push:

> branches: [main]

>

> jobs:

> deploy:

> runs-on: ubuntu-latest

> steps:

> - uses: actions/checkout@v4

> - uses: amondnet/vercel-action@v25

> with:

> vercel-token: secrets-vercel-token

> vercel-org-id: secrets-vercel-org-id

> vercel-project-id: secrets-vercel-project-id

> vercel-args: "--prod"

Deploy to a VPS via SSH

> deploy:

> runs-on: ubuntu-latest

> needs: [test, build]

> steps:

> - uses: actions/checkout@v4

> - uses: appleboy/ssh-action@v1

> with:

> host: secrets-server-host

> username: secrets-server-user

> key: secrets-ssh-private-key

> script: |

> cd /var/www/my-app

> git pull origin main

> npm ci --only=production

> npm run build

> pm2 restart my-app


Caching for Speed

Caching dependencies is the single biggest speedup for CI pipelines.

Node.js

The setup-node action has built-in caching:

> - uses: actions/setup-node@v4

> with:

> node-version: 20

> cache: "npm"

Custom Caching

For anything else, use the cache action:

> - uses: actions/cache@v4

> with:

> path: ~/.cache/some-tool

> key: tool-cache-hash-of-lockfile

> restore-keys: |

> tool-cache-


Scheduled Workflows

Run workflows on a schedule using cron syntax:

on:

schedule:

- cron: "0 9 1" # Every Monday at 9:00 UTC

Useful for:

  • Dependency update checks -- run npm audit or pip audit weekly
  • Stale issue cleanup -- close issues with no activity
  • Performance benchmarks -- track performance over time
  • Database backups -- trigger backup scripts on a schedule

Workflow Tips

Conditional Steps

> - run: npm run deploy

> if: github.ref == 'refs/heads/main' && github.event_name == 'push'

Manual Triggers

> on:

> workflow_dispatch:

> inputs:

> environment:

> description: "Environment to deploy to"

> required: true

> default: "staging"

> type: choice

> options:

> - staging

> - production

This adds a "Run workflow" button in the Actions tab with a dropdown to select the environment.

Reusable Workflows

If multiple repositories need the same CI pipeline, create a reusable workflow:

> # In a shared repo: .github/workflows/node-ci.yml

> on:

> workflow_call:

> inputs:

> node-version:

> required: false

> type: string

> default: "20"

Then call it from other repos:

> jobs:

> ci:

> uses: your-org/shared-workflows/.github/workflows/node-ci.yml@main

> with:

> node-version: "20"


Debugging Failed Workflows

  • Read the logs carefully. Click on the failed step to see the full output. The error is almost always in the last few lines.
  • Run locally. Use a tool like act to run GitHub Actions workflows on your machine before pushing.
  • Add debug logging. Set the ACTIONS_STEP_DEBUG secret to true for verbose output.
  • Check runner limits. Free GitHub runners have 7GB RAM and 14GB disk. If your build needs more, use larger runners or optimize your pipeline.

Free Tier Limits

GitHub Actions is free for public repositories. For private repositories:

  • 2,000 minutes/month on the Free plan
  • 3,000 minutes/month on the Team plan
  • 50,000 minutes/month on the Enterprise plan

macOS runners use 10x minutes (1 minute of macOS = 10 minutes from your quota). Linux runners are 1:1.

To stay within limits: cache aggressively, skip CI on documentation-only changes, and cancel redundant workflow runs with the concurrency setting.

For more developer guides and free tools, check out our blog and explore our developer tools.

Explore Our Free Tools & Games

Check out our curated collection of completely free browser games, tools, and extensions.

Browse Free Stuff

More Articles

Developer ToolsJSON

Stop Squinting at Messy JSON - Format It Instantly (Free Tool Inside)

Messy JSON is a productivity killer. Learn why formatting matters, common JSON pitfalls developers hit daily, and try our free browser-based JSON Formatter that works instantly with zero sign-ups.

7 min readRead More→
Browser GamesFree Games

Best Free Browser Games You Can Play Right Now in 2025

Discover the top free browser games of 2025 that require no downloads, no installs, and no sign-ups. From puzzle games to multiplayer adventures, these games run right in your browser.

8 min readRead More→
Developer ToolsFree Tools

Free Developer Tools Every Programmer Needs in Their Toolkit

A comprehensive guide to the best free developer tools available online. From JSON formatters to regex testers, these browser-based tools will supercharge your productivity.

10 min readRead More→
Chrome ExtensionsProductivity

10 Free Chrome Extensions That Will Boost Your Productivity

These free Chrome extensions will transform how you browse, work, and manage your time online. From tab management to dark mode, these extensions are must-haves.

7 min readRead More→