Blog
Jan 28, 2025 - 7 MIN READ
GitHub Actions CI/CD for Laravel: A Zero-Downtime Deployment Setup

GitHub Actions CI/CD for Laravel: A Zero-Downtime Deployment Setup

A practical walkthrough of atomic symlink deployments, parallel test pipelines, and one-command rollbacks using GitHub Actions and DigitalOcean.

Vincent Ndegwa

Vincent Ndegwa

I have seen great Laravel features fail for one simple reason: deployment was fragile. The code was correct, but release day was stressful because the process relied on manual SSH steps and luck.

This is the setup I now use to make releases boring, predictable, and reversible.

Deployment Requirements

  • test and deploy from GitHub Actions
  • no downtime for users
  • rollback in under one minute
  • same approach works for Laravel, Django, and FastAPI services

CI Pipeline First

Every push to main triggers tests. Deploy only runs if tests pass.

name: Deploy

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
      - run: composer install --no-interaction --prefer-dist
      - run: cp .env.testing .env && php artisan key:generate
      - run: php artisan test --parallel

  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.DEPLOY_HOST }}
          username: ${{ secrets.DEPLOY_USER }}
          key: ${{ secrets.DEPLOY_SSH_KEY }}
          script: /var/www/scripts/deploy.sh

Atomic Release Strategy

The server keeps timestamped releases:

  • /var/www/releases/20260330114500
  • /var/www/releases/20260330121810
  • /var/www/current -> symlink to active release

New code is prepared in a fresh folder. Only after migrations, cache warmup, and health checks pass do we switch the symlink.

ln -sfn /var/www/releases/$RELEASE /var/www/current
sudo systemctl reload php8.4-fpm

The symlink change is atomic, so active requests complete safely while new requests hit the new release.

Rollback Procedure

If health checks fail after deploy:

PREV=$(ls -t /var/www/releases | sed -n '2p')
ln -sfn /var/www/releases/$PREV /var/www/current
sudo systemctl reload php8.4-fpm

No panic, no patching in production. Just revert, recover, and inspect logs.

Secrets and Environment Safety

  • keep .env outside release folders
  • inject secrets via GitHub encrypted secrets
  • never expose internal AI services directly to the public network

Why This Matters for AI Features Too

If your Laravel app depends on FastAPI AI services, release reliability matters even more. Coordinated deploys and quick rollback protect both your core product and AI flows.

A stable CI/CD system is not just DevOps polish. It is product reliability, customer trust, and engineering sanity.

Vincent Ndegwa • Full-Stack Engineer & AI Engineer © 2026