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

Related Articles

Developer ToolsDocker

Docker for Developers -- From Zero to Containerized in 20 Minutes

Stop saying "it works on my machine." This hands-on guide walks you through Docker setup, writing Dockerfiles, Docker Compose for multi-service apps, and real-world patterns every developer needs.

14 min readRead More→
Developer ToolsJSON

JSON for Beginners - What It Is and Why Developers Love It

New to JSON? This beginner-friendly guide explains what JSON is, how it works, why it is everywhere, and how to work with it using free tools.

7 min readRead More→
AIDeveloper Tools

A Beginner Guide to Building With AI APIs in 2026

Want to add AI to your app? This beginner-friendly guide walks you through AI APIs, how they work, and how to make your first API call to Claude, GPT, and Gemini.

11 min readRead More→
Web DevelopmentDeveloper Tools

How to Make a Favicon for Your Website in 2026 (All Sizes, All Platforms)

That tiny icon in the browser tab matters more than you think. Learn what sizes you need, how to generate them all, and get the HTML code -- all for free.

7 min readRead More→

Latest from the Blog

GamesMultiplayer

The Best Free Games to Play With Friends and Family Online

No console, no downloads, no setup - just open a browser and play. The best free 2-player and vs-computer games to enjoy with friends and family.

May 18, 2026Read More→
GamesBrain Games

Daily Puzzle Games: How a 5-Minute Habit Sharpens Your Brain

Daily puzzle games like Word Guess and Word Groups turn brain training into a habit. Here is why a 5-minute daily puzzle works - and which free ones to play.

May 17, 2026Read More→
GamesClassic Games

12 Timeless Classic Games You Can Play Free Online

Solitaire, Minesweeper, Snake, Pong and more - the classic games that defined gaming, all playable free in your browser with no download or sign-up.

May 16, 2026Read More→