How to Build a CI/CD Pipeline with GitHub Actions: A Step-by-Step Guide

<h2>What is CI/CD?</h2>

<p>Continuous Integration (CI) and Continuous Deployment (CD) automate the process of testing, building, and deploying software. A well-configured pipeline catches bugs early, reduces manual work, and enables teams to ship confidently multiple times per day.</p>

<h2>Why GitHub Actions?</h2>

<p>GitHub Actions is built directly into GitHub, requiring zero external setup. With 15,000+ marketplace actions and native integration with every GitHub feature, it's the most accessible CI/CD platform available today.</p>

<h2>Core Concepts</h2>

<ul>

<li><strong>Workflow:</strong> A YAML file in <code>.github/workflows/</code> that defines your automation</li>

<li><strong>Job:</strong> A group of steps that run on the same runner machine</li>

<li><strong>Step:</strong> An individual task — run a shell command or invoke a marketplace action</li>

<li><strong>Runner:</strong> The VM that executes your workflow (ubuntu-latest, windows-latest, macos-latest)</li>

<li><strong>Event:</strong> What triggers the workflow — push, pull_request, schedule, or workflow_dispatch</li>

</ul>

<h2>Step 1: Basic CI Workflow</h2>

<p>Create <code>.github/workflows/ci.yml</code>. A basic CI workflow should:</p>

<ul>

<li>Trigger on <strong>push</strong> to main/develop branches and on <strong>pull_request</strong></li>

<li>Run a <strong>test job</strong> on ubuntu-latest</li>

<li>Use a <strong>matrix strategy</strong> to test across Node.js 18, 20, and 22</li>

<li>Steps: checkout code → setup Node.js with npm cache → run <code>npm ci</code> → lint → test → upload coverage</li>

</ul>

<h2>Step 2: Build and Push Docker Image</h2>

<p>Add a <strong>build job</strong> that runs after the test job passes (using <code>needs: test</code>) and only on the main branch:</p>

<ul>

<li>Use <code>docker/setup-buildx-action</code> for multi-platform build support</li>

<li>Authenticate with Docker Hub via <code>docker/login-action</code> using repository secrets</li>

<li>Build and push with <code>docker/build-push-action</code>, tagging with <code>latest</code> and the commit SHA</li>

<li>Enable GitHub Actions layer caching (<code>type=gha</code>) for significantly faster rebuilds</li>

</ul>

<h2>Step 3: Deploy to Production</h2>

<p>Add a <strong>deploy job</strong> that depends on the build job and targets a protected <code>production</code> environment:</p>

<ul>

<li>Use <code>appleboy/ssh-action</code> to SSH into your server with a private key stored in secrets</li>

<li>Pull the latest Docker image, stop and remove the old container, start the new one</li>

<li>Inject environment variables from GitHub Secrets at runtime</li>

</ul>

<h2>Step 4: Protect Your Production Environment</h2>

<p>In GitHub Settings → Environments → production, add safeguards:</p>

<ul>

<li><strong>Required reviewers:</strong> Designated team members must approve before the deploy job runs</li>

<li><strong>Wait timer:</strong> A 5-10 minute window to cancel if something looks wrong</li>

<li><strong>Branch restriction:</strong> Only deployments triggered from the main branch are allowed</li>

</ul>

<h2>Best Practices</h2>

<ul>

<li>Pin marketplace action versions to a full commit SHA to prevent supply-chain attacks</li>

<li>Use <code>npm ci</code> instead of <code>npm install</code> for reproducible, locked installs in CI</li>

<li>Never hardcode secrets — always use GitHub Encrypted Secrets</li>

<li>Chain jobs with <code>needs:</code> so downstream jobs only run when upstream jobs succeed</li>

<li>Use concurrency groups to auto-cancel redundant runs on the same branch</li>

<li>Run tests against a matrix of runtime versions to catch compatibility issues early</li>

</ul>

<h2>Conclusion</h2>

<p>GitHub Actions makes CI/CD approachable for any team. Start with a simple test workflow on your next pull request, add Docker builds when you containerize, then layer in deployment automation as your confidence grows. The investment pays off immediately through fewer production bugs and faster, safer releases.</p>