Automating Laravel 11 Deployment with GitHub Actions: A Comprehensive Guide
Deploy with Github Actions
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.
TL;DR;
Here is the gist: https://gist.github.com/sohag-pro/31d914fb56eb8a34d31eae82341571e6
Complete Workflow Code
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
Workflow Components Breakdown
1. Workflow Trigger
on:
push:
branches: [ develop ]
This section defines when the workflow runs - in this case, whenever code is pushed to the develop
branch.
2. Build and Test Job
Environment Setup
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.
Application Setup
- 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
Database Setup and Testing
- 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
3. Deployment Job
Pre-deployment Setup
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.
Deployment Process
- 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"
4. Required Server Setup
To make this workflow function properly, you'll need to set up the following on your server:
- Create the
deploy
directory with anafter.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
5. GitHub Secrets Setup
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
Best Practices Implemented
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
Troubleshooting Common Issues
- SSH Key Issues
# 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
- Permission Issues
# 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
Conclusion
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.