Tech Verse Logo
Enable dark mode
From GitHub Actions to Production Rollout: CI/CD for Laravel

From GitHub Actions to Production Rollout: CI/CD for Laravel

Tech Verse Daily

Tech Verse Daily

4 min read

Automate, deploy, and scale Laravel projects confidently with GitHub Actions, staging environments, and zero-downtime rollouts.

Introduction

Manual deployments are slow, error-prone, and often lead to those dreaded “it works on my machine” moments. For modern Laravel applications — especially in production — continuous integration and delivery (CI/CD) pipelines have become essential.

In this post, we’ll walk through how to set up a robust CI/CD pipeline for Laravel using GitHub Actions, staging environments, and zero-downtime deployment strategies. You’ll also learn how to handle database migrations safely and perform rollbacks without panic.

Understanding the Laravel Deployment Lifecycle

Before jumping into automation, it’s important to understand what happens when you deploy Laravel:

  1. Code Update — Fetching latest code from Git.

  2. Dependency Installation — Running composer install and npm install.

  3. Environment Configuration — Loading .env values and clearing caches.

  4. Database Migrations — Updating schema using php artisan migrate.

  5. Asset Compilation — Running npm run build for frontend assets.

  6. Cache Optimization — Running php artisan config:cache and php artisan route:cache.

A proper CI/CD pipeline automates these steps while ensuring consistency between environments.

Setting Up a GitHub Actions Workflow

Let’s start with a basic GitHub Actions workflow for a Laravel project.

Create a file at:
.github/workflows/deploy.yml

name: Deploy Laravel App

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: 8.3
          extensions: mbstring, intl, bcmath, pdo_mysql

      - name: Install Composer dependencies
        run: composer install --no-dev --optimize-autoloader

      - name: Run tests
        run: php artisan test

      - name: Deploy to server
        uses: appleboy/scp-action@v0.1.4
        with:
          host: ${{ secrets.SERVER_IP }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_KEY }}
          source: "."
          target: "/var/www/laravel-app"

Explanation

  • Trigger: Runs when changes are pushed to the main branch.

  • Setup: Installs PHP, dependencies, and runs tests.

  • Deploy: Uses scp to copy files securely to your server.

This is the starting point — next, we’ll make it smarter.

Introducing Staging Environments

Before you deploy to production, you need a staging environment — a clone of production where you can test the release safely.

Branch strategy example:

  • develop → auto-deploys to staging.

  • main → auto-deploys to production (after review).

In your workflow:

on:
  push:
    branches:
      - main
      - develop

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      ...
      - name: Set environment target
        run: |
          if [ "${{ github.ref_name }}" = "main" ]; then
            echo "DEPLOY_PATH=/var/www/laravel-prod" >> $GITHUB_ENV
          else
            echo "DEPLOY_PATH=/var/www/laravel-staging" >> $GITHUB_ENV
          fi
      - name: Deploy
        uses: appleboy/scp-action@v0.1.4
        with:
          host: ${{ secrets.SERVER_IP }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_KEY }}
          source: "."
          target: ${{ env.DEPLOY_PATH }}

Now your workflow deploys automatically to the correct environment based on branch.

Zero-Downtime Deployment (Like a Pro)

The biggest risk during deployment is downtime — especially when running migrations or clearing caches.

You can achieve zero-downtime deployment using tools like:

  • Envoy (Laravel’s built-in SSH task runner)

  • Deployer (a PHP-based deployment tool)

Example: Using Envoy

Create Envoy.blade.php at your project root:

@servers(['web' => 'deploy@your-server-ip'])

@task('deploy', ['on' => 'web'])
    cd /var/www/laravel-app
    git pull origin main
    composer install --no-dev --optimize-autoloader
    php artisan migrate --force
    php artisan cache:clear
    php artisan config:cache
    php artisan route:cache
    php artisan view:cache
@endtask

Then trigger it via GitHub Actions:

- name: Run Envoy Deployment
  run: php vendor/bin/envoy run deploy --server=web

This approach executes commands remotely and keeps your app online during updates.

Safe Database Migrations

Migrations can be risky in production — especially when they modify large tables.

Best practices:

  • Always run migrations inside maintenance mode if there’s a schema change:

php artisan down
php artisan migrate --force
php artisan up
  • Use --force to skip confirmation prompts.

  • For complex schema changes, use rolling migrations (e.g., add nullable columns first, then fill data later).

  • Automate with your CI/CD, not manual SSH commands.

Handling Rollbacks Gracefully

Things can go wrong — a failed migration, missing environment variable, or misbehaving feature.

Here’s how to roll back safely:

  1. Versioned releases: Keep previous releases in /var/www/releases with symlinks.

  2. Switch instantly:

ln -sfn /var/www/releases/previous /var/www/current

3. Rollback migrations:

php artisan migrate:rollback --force

You can automate all of this using Deployer or Envoy tasks for consistency.

Adding Notifications

A good CI/CD pipeline should talk back.

You can add a step to send Slack or Discord notifications when a deployment completes:

- name: Notify Slack
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    fields: repo,message,commit,author
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

This ensures visibility across your team when something goes live.

Security & Secrets Management

Never hardcode credentials in your workflow. Use GitHub Secrets for environment variables like:

  • SERVER_IP

  • SERVER_USER

  • SSH_KEY

  • SLACK_WEBHOOK

For more advanced setups, integrate with AWS Secrets Manager, Vault, or Doppler.

    Latest Posts

    View All

    React 19: What’s new in React 19

    React 19: What’s new in React 19

    Laravel Strict Validation: Enforcing Exact PHP Types

    Laravel Strict Validation: Enforcing Exact PHP Types

    Next.js 16.0.1: The Essential Update Developers Shouldn’t Skip

    Next.js 16.0.1: The Essential Update Developers Shouldn’t Skip

    Time Interval Helpers in Laravel 12.40

    Time Interval Helpers in Laravel 12.40

    From GitHub Actions to Production Rollout: CI/CD for Laravel

    From GitHub Actions to Production Rollout: CI/CD for Laravel

    Top React Libraries and Frameworks Every Frontend Developer Should Know

    Top React Libraries and Frameworks Every Frontend Developer Should Know

    PHP 8.5 New Features and Deprecations

    PHP 8.5 New Features and Deprecations

    Manage, Track and Monitor Queue Jobs in Laravel with Vantage

    Manage, Track and Monitor Queue Jobs in Laravel with Vantage

    Tinkerwell 5: Introducing Tinkerwell Intelligence

    Tinkerwell 5: Introducing Tinkerwell Intelligence

    Roach PHP - Complete Web Scraping toolkit for PHP

    Roach PHP - Complete Web Scraping toolkit for PHP