Automating Laravel 11 Deployment with GitHub Actions: A Comprehensive Guide

WhoAmI => notes.sohag.pro/author
Search for a command to run...

WhoAmI => notes.sohag.pro/author
Excellent post on automating pipelines for GitHub actions! This will definitely help streamline deployments and improve efficiency—thanks for sharing!
That is a nice informative blog thank you https://www.softcrayons.com/cloud-computing
I had a solid list of reasons my life wasn't moving faster, until a grainy old lecture pointed out the one name missing from it

The finale isn't a victory lap. It's the story of the control I shipped that did nothing, the footgun still sitting in my demo, and the handful of things I'd keep exactly as they are.

How do you hold a large payment for a second pair of eyes without ever letting the unapproved money touch a balance, and how do you stream that decision to the outside world without standing up a broker?

How do you show the total under a parent account when the whole system refuses to store a balance? A recursive query, a trigger that refuses to draw a circle, and a rule about what actually has to sum to zero.

Every time I wanted to change an FX rate I had to edit a file on the server and restart the app. So I moved rates and markup into a live admin API, and then audited it hard enough to find the bug that quietly undid the whole thing.

In modern web development, automated deployment pipelines are crucial for maintaining efficient and reliable software delivery. Today, we'll explore a robust GitHub Actions workflow designed specifically for Laravel 11 applications. This workflow automates testing and deployment processes, ensuring code quality and smooth deployments to development servers.
Here is the gist: https://gist.github.com/sohag-pro/31d914fb56eb8a34d31eae82341571e6
First, let's look at the complete workflow file that should be placed in .github/workflows/deploy.yml:
name: Deploy to dev server
on:
push:
branches: [ develop ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
ref: develop
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
- name: Copy .env
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
- name: Install Dependencies
run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist --ignore-platform-req=ext-imagick
- name: Generate key
run: php artisan key:generate
- name: Directory Permissions
run: chmod -R 777 storage bootstrap/cache
- name: Create Database
run: |
mkdir -p database
touch database/database.sqlite
- name: Migrate
env:
DB_CONNECTION: sqlite
DB_CONNECTION_LOG: sqlite
DB_DATABASE: database/database.sqlite
DB_LOG_DATABASE: database/database.sqlite
run: php artisan migrate
- name: Passport Install
env:
DB_CONNECTION: sqlite
DB_DATABASE: database/database.sqlite
run: php artisan passport:install
- name: Clear Cache
run: php artisan optimize:clear
- name: Run tests
env:
DB_CONNECTION: sqlite
DB_CONNECTION_LOG: sqlite
DB_DATABASE: database/database.sqlite
DB_LOG_DATABASE: database/database.sqlite
run: php artisan test
deploy:
needs: build-and-test
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
ref: develop
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
- name: Install Dependencies
run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist --ignore-platform-req=ext-imagick
- name: Directory Permissions
run: chmod -R 777 storage bootstrap/cache deploy
- name: Pre Deploy
uses: appleboy/ssh-action@master
with:
username: ${{ secrets.DEV_REMOTE_USER }}
host: ${{ secrets.DEV_REMOTE_HOST }}
key: ${{ secrets.DEV_SSH_PRIVATE_KEY }}
script: |
cd ${{ secrets.DEV_REMOTE_TARGET }} && php artisan down
- name: Deploy to Server
uses: easingthemes/ssh-deploy@main
with:
SSH_PRIVATE_KEY: ${{ secrets.DEV_SSH_PRIVATE_KEY }}
ARGS: "-rlgoDzvc -i --delete"
SOURCE: "/"
REMOTE_HOST: ${{ secrets.DEV_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.DEV_REMOTE_USER }}
TARGET: ${{ secrets.DEV_REMOTE_TARGET }}
EXCLUDE: "/storage/, /bootstrap/, .env"
- name: Post Deploy
uses: appleboy/ssh-action@master
with:
username: ${{ secrets.DEV_REMOTE_USER }}
host: ${{ secrets.DEV_REMOTE_HOST }}
key: ${{ secrets.DEV_SSH_PRIVATE_KEY }}
script: |
cd ${{ secrets.DEV_REMOTE_TARGET }} && ./deploy/after.sh
on:
push:
branches: [ develop ]
This section defines when the workflow runs - in this case, whenever code is pushed to the develop branch.
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
ref: develop
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
This sets up a fresh Ubuntu environment and configures PHP 8.2 for Laravel 11.
- name: Copy .env
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
- name: Install Dependencies
run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist --ignore-platform-req=ext-imagick
- name: Generate key
run: php artisan key:generate
- name: Directory Permissions
run: chmod -R 777 storage bootstrap/cache
These steps prepare the Laravel application by:
Creating the environment file
Installing dependencies
Generating the application key
Setting proper directory permissions
- name: Create Database
run: |
mkdir -p database
touch database/database.sqlite
- name: Migrate
env:
DB_CONNECTION: sqlite
DB_CONNECTION_LOG: sqlite
DB_DATABASE: database/database.sqlite
DB_LOG_DATABASE: database/database.sqlite
run: php artisan migrate
This section:
Creates a SQLite database for testing
Sets up environment variables
Runs migrations
deploy:
needs: build-and-test
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
ref: develop
The deployment job only runs after successful build and testing.
- name: Pre Deploy
uses: appleboy/ssh-action@master
with:
username: ${{ secrets.DEV_REMOTE_USER }}
host: ${{ secrets.DEV_REMOTE_HOST }}
key: ${{ secrets.DEV_SSH_PRIVATE_KEY }}
script: |
cd ${{ secrets.DEV_REMOTE_TARGET }} && php artisan down
- name: Deploy to Server
uses: easingthemes/ssh-deploy@main
with:
SSH_PRIVATE_KEY: ${{ secrets.DEV_SSH_PRIVATE_KEY }}
ARGS: "-rlgoDzvc -i --delete"
SOURCE: "/"
REMOTE_HOST: ${{ secrets.DEV_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.DEV_REMOTE_USER }}
TARGET: ${{ secrets.DEV_REMOTE_TARGET }}
EXCLUDE: "/storage/, /bootstrap/, .env"
To make this workflow function properly, you'll need to set up the following on your server:
deploy directory with an after.sh script:#!/bin/bash
# after.sh - Place this in your deploy directory
# Install/update dependencies
composer install --no-interaction --prefer-dist --optimize-autoloader
# Clear caches
php artisan cache:clear
php artisan config:clear
php artisan view:clear
php artisan route:clear
# Restart queue workers
php artisan queue:restart
# Run migrations
php artisan migrate --force
# Clear optimizations
php artisan optimize:clear
# Rebuild cache
php artisan optimize
# Bring application back online
php artisan up
In your GitHub repository settings, add these secrets:
DEV_REMOTE_USER=your-server-username
DEV_REMOTE_HOST=your-server-hostname
DEV_SSH_PRIVATE_KEY=your-ssh-private-key
DEV_REMOTE_TARGET=/path/to/your/application
Separation of Concerns: Build/test and deployment are separate jobs
Environment Isolation: Uses fresh Ubuntu instance for each run
Security: Sensitive data stored in GitHub Secrets
Zero-downtime Deployment: Uses maintenance mode during deployment
Artifact Management: Proper exclusion of environment-specific files
Error Prevention: Comprehensive testing before deployment
# Generate new SSH key pair
ssh-keygen -t rsa -b 4096 -C "your-email@example.com"
# Add public key to server's authorized_keys
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
# Add private key to GitHub secrets
cat ~/.ssh/id_rsa
# On your server, ensure proper ownership
sudo chown -R www-data:www-data /path/to/your/application
sudo chmod -R 775 /path/to/your/application/storage
sudo chmod -R 775 /path/to/your/application/bootstrap/cache
This GitHub Actions workflow provides a robust, automated pipeline for Laravel 11 applications. It ensures code quality through automated testing and provides a reliable deployment process to development servers. By implementing this workflow, teams can focus more on development while having confidence in their deployment process.
The workflow can be further customized based on specific needs, such as:
Adding additional testing stages
Implementing staging environments
Adding notification systems
Including code quality checks
Remember to always test your deployment workflow thoroughly in a safe environment before implementing it in production scenarios.