Skip to content

How to Set Up Your First CI/CD Pipeline with GitHub Actions: A Step-by-Step Guide

I used to think CI/CD pipelines required dedicated servers, webhook configurations, and days of setup. Jenkins scared me. I avoided automation because the infrastructure overhead seemed overwhelming for a solo developer. Then I tried GitHub Actions and discovered that the entire pipeline activates by dropping one YAML file into my repository.

That realization changed how I approach deployment. I no longer manually run tests before pushing. I no longer ssh into servers to deploy. The pipeline does it for me, every time I commit.

Why I Delayed CI/CD Adoption

Traditional CI/CD tools demand infrastructure. Jenkins needs a server. TeamCity needs installation. CircleCI requires configuration files scattered across multiple systems. As someone working on personal projects, I kept asking: is all this overhead worth it?

What I missed was that GitHub Actions eliminates the infrastructure question entirely. It runs inside GitHub. No servers to provision. No webhooks to configure. No security patches to apply. The pipeline lives in my repository alongside my code.

Brian Douglas from GitHub puts it simply: “You just drop one file in your repo, and it works.” That’s the entire setup.

Step 1: Prepare Your Repository

I started with an existing Node.js project. You can use any repository with a recognizable build process. GitHub Actions detects your technology stack and suggests appropriate templates.

If you don’t have a project ready, fork an open-source repository or create a new one. The key requirement is having code that can be tested or built. For my setup, I used a simple Express API with npm test defined.

Step 2: Select a Workflow Template

Navigate to your repository on GitHub and click the Actions tab. GitHub analyzes your project and suggests workflow templates matching your detected technology. For my Node.js project, I saw suggestions for Node.js CI, Node.js with npm, and several others.

I clicked “Node.js CI” and GitHub created a starter workflow file in .github/workflows/. The template included everything I needed: setup steps, dependency installation, and test execution.

Here’s what the generated workflow looked like:

.github/workflows/ci.yml
name: Development Workflow
on:
pull_request:
types: [opened, edited, synchronized, reopened]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
- run: npm run lint

Each line matters. actions/checkout@v4 pulls my code into the runner. actions/setup-node@v4 installs Node.js version 20. npm ci installs dependencies with exact versions from lockfile. Then tests run, then linting.

The template handles the hard parts. I didn’t write any of this from scratch. I just clicked a button.

Step 3: Trigger the Pipeline

The pipeline runs automatically when I push code. For the workflow above, any pull request triggers execution. I created a branch, made a small change, and opened a PR.

Within seconds, the Actions tab showed my workflow running. A yellow circle indicated active execution. When tests passed, it turned green. When they failed, it turned red with links to the exact failing step.

I also added a deployment workflow that triggers on main branch pushes:

.github/workflows/release.yml
name: Release
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build
- run: npm run deploy

This workflow builds and deploys whenever I merge to main. The two workflows together form a complete pipeline: CI on pull requests, deployment on merge.

Step 4: Monitor Execution

The workflow visualizer became my primary debugging tool. When a pipeline fails, I click through each job and step to see exactly where it broke. Logs show timestamps, error messages, and the command that failed.

I learned to check the visualizer before assuming my code was wrong. Several times the failure was environmental: missing environment variables, incorrect Node version, or a dependency that wasn’t in package.json.

The logs pinpoint failure locations with timestamps and full output. I scroll to the red section, read the error, and fix it in my code. Then push again. The cycle takes minutes, not hours.

Mistakes I Made

Writing YAML From Scratch

My first attempt failed because I tried composing the workflow manually without understanding the syntax. I missed required fields, used wrong indentation, and referenced actions that didn’t exist. The template approach avoids all this. Start with templates, then customize incrementally.

Overcomplicating the First Pipeline

I initially wanted my workflow to run tests, lint, build, deploy, send notifications, and update documentation. It failed because I skipped basics. Start simple: just tests. Add linting. Add build. Expand once each piece works.

Ignoring the Logs

When my first workflow failed, I assumed my tests were broken and spent hours debugging test code. The actual problem: I hadn’t set up Node.js correctly in the workflow. The logs showed the exact error immediately. I wasted time by not reading them.

What This Pipeline Gives Me

Every pull request now runs tests automatically. Every merge to main builds and deploys. I no longer wonder if I forgot to run tests before pushing. I no longer manually deploy to servers. The pipeline enforces consistency that manual processes cannot.

The confidence this provides matters more than the automation itself. I know merged code passes tests. I know deployed code matches what I intended. That certainty changes how I develop.

Starting Your Own Pipeline

Pick a repository with tests already defined. Go to the Actions tab. Click a suggested template. Push a commit. Watch it run. That’s the entire process.

You don’t need infrastructure. You don’t need webhook configuration. You don’t need enterprise-grade complexity. You need one YAML file and a willingness to iterate when things fail.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments