CI/CD for Laravel: Git workflow & zero-downtime deploys
By Andrea Pollastri · Last updated: · free to read, no paywall
The gap between "it works on my machine" and "it deploys itself safely" is smaller than ever. This guide gives you a Git workflow that scales down to a team of one, a copy-paste CI pipeline, and the zero-downtime deployment patterns that make Friday releases boring.
Why pipelines pay off (even solo)
CI/CD isn't enterprise ceremony — it's the cheapest insurance a codebase can buy:
- Repeatability: the pipeline runs the same checks every time; humans don't.
- Deploy anxiety goes to zero. When deploys are one push and rollbacks are one symlink, you ship small changes often instead of scary batches rarely.
- Review quality rises because CI has already caught the mechanical problems before a human looks.
- AI-generated code makes this non-optional: if agents write more of your code, the pipeline is the referee that keeps standards objective (more in the spec-driven development guide).
A Git workflow that scales down
Skip git-flow unless you ship boxed releases. For web apps, trunk-based with short-lived branches wins:
mainis always deployable. Protect it: no direct pushes, PRs must pass CI.- Branches live days, not weeks.
feature/checkout-vat,fix/queue-timeout— merge or delete. - Conventional commits (
feat:,fix:,chore:) make history scannable and changelogs automatable. - Tag releases (
v2026.08.04or semver) so you can always answer "what was live on Tuesday?". - PRs are the audit trail. Even solo: a two-minute self-review with CI green catches a surprising amount.
The CI pipeline, copy-paste ready
Four stages, each failing fast: style, static analysis, tests, security audit. Here's a complete GitHub
Actions workflow for a Laravel app (drop it in .github/workflows/ci.yml):
name: CI
on:
pull_request:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
extensions: mbstring, xml, curl, zip, intl, pdo_sqlite
coverage: none
- name: Install dependencies
run: composer install --prefer-dist --no-progress
- name: Prepare environment
run: |
cp .env.example .env
php artisan key:generate
- name: Code style (Pint)
run: vendor/bin/pint --test
- name: Static analysis (Larastan)
run: vendor/bin/phpstan analyse --memory-limit=1G
- name: Tests (Pest)
run: php artisan test --parallel
- name: Security audit
run: composer audit
Notes worth stealing:
- Pint, Larastan, Pest, audit — in that order: cheapest checks first, so failures are fast.
--parallelon Pest typically halves suite time for free.composer auditfails the build when a dependency has a known CVE — your supply-chain tripwire (see the security checklist for the full dependency hygiene section, including Laravel-specific scanning with Checkpoint).- Add
npm ci && npm run buildas a separate job if your assets build is nontrivial.
Zero-downtime deployment options
Zero-downtime means: build the new release next to the old one, swap atomically, restart workers. Three ways to get it:
1. Webhook push-to-deploy (recommended default)
Your Git host calls a webhook on push; the server builds a new release in releases/, runs
composer install, migrate --force and caches, swaps the current
symlink and restarts queue workers. This is exactly what Cipi configures
automatically when you connect GitHub or GitLab to an app — deploy keys, webhook, releases and symlink
swap included, plus Node asset builds on deploy and pre-deploy database snapshots in Cipi 5. Setup cost:
one cipi app create.
2. CI-driven deploy (SSH from Actions)
The pipeline builds artifacts and pushes them over SSH (Deployer or rsync + script). More control, more YAML to own — worth it when you need build artifacts identical across environments.
3. The hybrid
CI runs tests; on green, it hits the deploy webhook. You get gated deploys without moving the deployment
logic into CI. With Cipi, that's a one-line curl at the end of the workflow.
After the deploy
- Health checks: verify the app answers before you celebrate — Cipi 5 ships app health checks you can point monitors at.
- Smoke test the critical path: one
curlto the login page and one to an authenticated endpoint catch most "deployed but broken" states. - Watch exceptions for 15 minutes. A self-hosted tracker like Boogle makes the post-deploy error spike visible immediately.
- Notify the team: deploy notifications to Slack or Telegram close the loop (Cipi's docs include ready-made CI notification examples).
Migrations & rollback strategy
- Expand/contract: add the new column and code that writes both first; remove the old one releases later. Never rename in place.
- Destructive migrations get their own release — after the code that stopped using the data is verified in production.
- Code rollback is the symlink back to the previous release; that's why the releases pattern matters.
- Data rollback is a snapshot. Cipi 5 takes a pre-deploy database snapshot automatically, so the worst migration mistake has an undo button.
Put this into practice with Cipi
Cipi is the free, open-source deploy CLI referenced throughout this guide: one command turns a fresh Ubuntu VPS into a hardened production server for Laravel — Nginx, PHP-FPM or Octane, MariaDB or PostgreSQL, queues, scheduler, SSL and zero-downtime Git deploys included.
Frequently asked questions
Do I need CI/CD as a solo Laravel developer?
Yes — arguably more than a team does, because nobody reviews your work. A pipeline that runs Pint, Larastan, Pest and composer audit on every push is a tireless second pair of eyes, and webhook-based deploys mean shipping stops being a manual ritual.
How do I get zero-downtime deployments for Laravel?
Use the releases pattern: build the new version in its own directory, run migrations and caches, then atomically swap a symlink and restart queue workers. Tools like Cipi configure this automatically for GitHub and GitLab pushes, so you get zero-downtime deploys without writing deployment scripts.
How long should a Laravel CI pipeline take?
Under five minutes for the common case, or developers start bypassing it. Run Pest in parallel, cache Composer dependencies, and move slow browser tests to a nightly job instead of blocking every PR.
Are database migrations safe with zero-downtime deploys?
Yes, with the expand/contract pattern: make additive changes first, deploy code that works with both schemas, and remove old columns in a later release. Keep destructive changes in their own deploy, and take a pre-deploy snapshot (automatic in Cipi 5) so mistakes are reversible.
Is it safe to deploy on Fridays?
With small diffs, a green pipeline, health checks and one-command rollback — yes. The Friday fear is a symptom of big-batch, manual deployments; fix the process and the day of the week stops mattering.