CI/CD with GitHub Actions That Teams Trust
Pipelines should fail fast, deploy predictably, and never leak secrets in logs. GitHub Actions is enough for most Laravel/Next.js monorepos until you outgrow concurrent runner limits.
Laravel Deploy Workflow
# .github/workflows/deploy.yml
name: Deploy Production
on:
push:
branches: [main]
jobs:
test-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mbstring, pdo_mysql, redis
- name: Install dependencies
run: composer install --prefer-dist --no-progress
- name: Run tests
run: php artisan test
- name: Deploy via SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.DEPLOY_HOST }}
username: deploy
key: ${{ secrets.DEPLOY_KEY }}
script: |
cd /var/www/app
git pull origin main
composer install --no-dev -o
php artisan migrate --force
php artisan config:cache
sudo systemctl reload php8.3-fpm
Pipeline Design Principles
- Run PHPUnit/Vitest on every PR—block merge on failure
- Cache Composer and npm dependencies by lockfile hash
- Separate staging deploy on
develop, production on tagged releases - Use environments with required reviewers for production
Add smoke tests post-deploy—curl health endpoint, verify HTTP 200. One team shipped broken .env for three hours because deploy succeeded but app boot looped.
Branch Protection and Quality Gates
Require PR reviews, passing CI, and up-to-date branches before merge to main. Block force pushes. Enforce conventional commits if you generate changelogs automatically—future you will thank present you.
Parallelize jobs: lint, unit tests, and frontend build can run concurrently. Fail fast on lint before expensive integration tests hitting real databases. Use service containers for MySQL in CI matching production version.
Store deployment history: who deployed what SHA when. Rollback script should accept previous tag argument and run in under five minutes with smoke test verification automated in the pipeline.
Cache Docker layer builds with buildx and registry cache for faster deploy pipelines—rebuilding Composer vendor from scratch every push wastes eight minutes teams tolerate until someone measures. Artifact immutability plus digest pinning closes supply chain gaps cheaply.
Secrets and Environments
GitHub environments with protection rules gate production deploys—require reviewer from on-call rotation, not any team member clicking approve. OIDC to AWS eliminates static AWS_ACCESS_KEY_ID in secrets rotating annually if someone remembers.
Artifact signing with cosign for container images verifies deploy pipeline integrity—supply chain attacks target CI because developers trust its output implicitly. Start signing before compliance asks, not after incident.
Use concurrency groups to cancel in-progress deploys when newer commits land—prevents out-of-order releases leaving production on older SHAs until someone notices missing features during a demo.
Database in CI
Run migrations against ephemeral MySQL service container matching production major version. Seed minimal fixtures for integration tests—full production dumps in CI leak PII and slow pipelines. Snapshot schema with squawk or similar linter catching destructive migrations before merge protects Friday deploys.
Pin action versions to commit SHA for security-critical workflows, not @v4 floating tags—supply chain attacks target popular actions. Dependabot can bump SHA with PR review same as application dependencies. Treat CI config as production code because it is.
Notify Slack on deploy success and failure with commit message and author—visibility reduces mystery about what changed when support tickets spike twenty minutes after green pipeline falsely assumed everything fine without smoke test.
Review workflow durations monthly—runs creeping from eight to twenty minutes usually mean missing caches or duplicated steps copied across workflows without extracting shared reusable workflows yet.
Reusable Workflows
Extract shared PHP setup and Composer cache into reusable workflows so three copy-pasted YAML files do not drift. Tag production releases in git; rollbacks should be git checkout v1.4.2 && deploy, not manual server edits.
Frequently Asked Questions
GitHub Actions vs Jenkins?
Actions wins for GitHub-native repos with lower maintenance. Jenkins fits complex on-prem requirements or exotic integrations.
How do we handle database migrations in CI/CD?
Run migrations once per deploy on single leader job—not parallel on every replica.
Secrets best practice?
GitHub encrypted secrets + OIDC to AWS for temporary credentials—avoid long-lived AWS keys in secrets.