When I was a junior developer, I heard "CI/CD pipeline" and immediately pictured some DevOps wizard doing infrastructure magic I'd never understand. I nodded along in meetings and Googled it later, only to find documentation written for people who already knew what it meant.
This series exists so you don't have to do that.
By the end of seven posts you'll have a working GitHub Actions pipeline that runs your tests against a real MySQL database, enforces code style, does static analysis, and deploys to a VPS — the same setup I use on production Laravel projects today.
The Problem CI/CD Solves
Picture this. You're on a team of three developers. Alice pushes a feature branch. Bob merges his own branch an hour later. Neither of them ran the tests. On Friday afternoon, you merge both to main and deploy. The site is down by midnight because Alice's migration clashed with Bob's model change, and neither worked in isolation.
This is the "it works on my machine" problem scaled to a team. CI/CD is the systematic answer.
CI — Continuous Integration means every push to the repository triggers an automated check. Run the tests. Check the code style. Run static analysis. If any step fails, the push is rejected before it ever reaches main.
CD — Continuous Delivery/Deployment is the second half. Once a push passes CI, automatically ship it to a staging or production server. No manual FTP. No SSH-in-and-run-artisan. The pipeline handles it.
The Mental Model That Made It Click for Me
Think of CI/CD as a factory production line.
Raw material (your code) enters at one end. At each station, a robot (a GitHub Actions job) does a quality check:
- Does it compile? (Composer install, npm install)
- Does it look right? (Pint formatting, ESLint)
- Does it work correctly? (Pest/PHPUnit test suite)
- Does it analyse cleanly? (PHPStan)
- Is it safe to ship? (Security audit)
Only code that passes every station reaches the end of the line — the production server. If any station rejects it, the line stops and you get notified immediately.
The alternative is doing all those checks manually, inconsistently, or not at all. I've been on projects where "deployment" meant one developer SSHing into the server, doing git pull, and hoping for the best. Something breaks at 2am. No-one knows what changed. Everyone blames each other. CI/CD eliminates that class of problem entirely.
The Three Things You Need
To follow this series you need:
- A Laravel project in a GitHub repository
- A GitHub account (the free tier includes 2,000 CI minutes per month — enough for a small team)
- Eventually, a VPS (we cover this in Post 6 — DigitalOcean, Hetzner, or any Ubuntu 22.04+ server works)
You do not need to already know YAML. You do not need DevOps experience. You need to be able to read a Laravel controller and write a basic Pest test. That's it.
How GitHub Actions Fits In
GitHub Actions is GitHub's built-in CI/CD engine. You write workflow files in YAML inside .github/workflows/. Every time you push, GitHub spins up a fresh Ubuntu virtual machine, runs your YAML instructions, and reports back pass or fail on the pull request.
The key insight: that VM starts empty. It has no PHP, no Composer, no MySQL, no .env file. Your workflow has to install everything from scratch on every run. This is actually a feature, not a bug — it means your pipeline is reproducible. If it passes in CI, it will pass on any fresh server too.
Here's the skeleton of a workflow file:
name: API CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
tests:
name: Run Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
- name: Install dependencies
run: composer install
- name: Run tests
run: ./vendor/bin/pest
In the next post we'll build this out to a production-grade workflow. For now, just recognise the structure: trigger → jobs → steps.
What We're Building in This Series
Here's the roadmap. Each post is a standalone tutorial you can follow:
| # | Post | What you'll have after |
|---|---|---|
| 1 | This post | The mental model |
| 2 | First workflow | A YAML file that runs on push |
| 3 | Tests with MySQL | Pest against a real DB in CI |
| 4 | Code quality gates | Pint + PHPStan blocking merges |
| 5 | Secrets and env vars | Secure config management |
| 6 | VPS deployment | Zero-downtime ship via SSH |
| 7 | Debugging CI failures | Systematic failure diagnosis |
Let's build it.
Originally published at dineshstack.com — read the full version with code samples and updates there.
Top comments (0)