CI/CD with GitHub Actions for Laravel + Next.js Projects
My complete CI/CD pipeline for full-stack projects: automated tests, code quality checks, staging deploys on PR, and production deploys on merge — all in GitHub Actions.
Manual deployments are the enemy of shipping confidently. Every project I start gets a CI/CD pipeline before the first line of application code. GitHub Actions is my tool of choice — it's free for public repos, tightly integrated with PRs, and expressive enough for complex multi-service deployments. Here's the pipeline I actually use.
Pipeline Overview
- On every push: lint, typecheck, unit tests (fast feedback, under 2 minutes)
- On pull request: full test suite + staging deploy to a preview environment
- On merge to main: production deploy to both Laravel (Forge) and Next.js (Vercel)
- Nightly: database backup job and dependency audit
Laravel Test Job
jobs:
test-laravel:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_DATABASE: test
MYSQL_ROOT_PASSWORD: password
options: --health-cmd="mysqladmin ping"
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: pdo_mysql, zip
- run: composer install --no-interaction
- run: cp .env.ci .env && php artisan key:generate
- run: php artisan migrate --force
- run: php artisan test --parallelNext.js Build and Type Check
test-nextjs:
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
- run: npm run typecheck
- run: npm run buildDeployment Gate: Required Status Checks
In GitHub repository settings, set both test jobs as required status checks on the main branch. No merge is possible if either fails. This rule saved me from a broken production deploy at least a dozen times last year — someone (usually me) tries to push a quick fix and the pipeline catches an unrelated regression.
Secrets Management
Store deployment keys, SSH private keys, and API tokens as GitHub Actions secrets — never in workflow YAML. For per-environment configuration, use GitHub Environments (staging/production) with environment-specific secrets and required reviewers for production deployments. This adds an approval gate before any production release.
Caching for Speed
Cache composer dependencies with actions/cache keyed on composer.lock, and npm dependencies with the built-in cache option on setup-node. This cuts my CI time from 8 minutes to under 3 minutes on a warm cache. For the Next.js build, caching the .next/cache directory between runs shaves another 90 seconds off incremental builds.