Setting up a robust CI/CD pipeline for Laravel applications is crucial for maintaining code quality, accelerating development cycles, and ensuring smooth, reliable deployments. This guide will walk you through building an effective pipeline using GitHub Actions, leveraging Docker for consistent environments, and incorporating automated testing.
---
Why CI/CD for Laravel?
Continuous Integration (CI) and Continuous Delivery/Deployment (CD) automate critical steps in the software development lifecycle. For Laravel projects, this translates to:
- Faster Feedback Loops: Catch errors early with automated tests.
- Consistent Environments: Docker ensures your application behaves the same from development to production.
- Reduced Manual Errors: Automate repetitive deployment tasks.
- Increased Confidence: Deploy with assurance knowing your code has passed all checks.
---
Core Components of Our Pipeline
We'll focus on these key technologies:
- GitHub Actions: Our CI/CD orchestrator.
- Docker: For containerizing the Laravel application and its dependencies.
- PHPUnit: Laravel's built-in testing framework.
- Composer: PHP dependency management.
- Artisan: Laravel's command-line interface.
---
Step 1: Project Setup and Dockerization
First, ensure your Laravel project is set up to be Dockerized. This typically involves a Dockerfile
for your application and a docker-compose.yml
for local development.
Example Dockerfile
# For example, using an official PHP-FPM image
FROM php:8.2-fpm-alpine
# Install system dependencies
RUN apk add --no-cache \
nginx \
mysql-client \
nodejs \
npm
# Install PHP extensions
RUN docker-php-ext-install pdo_mysql opcache
# Set working directory
WORKDIR /var/www/html
# Copy composer.lock and composer.json
COPY composer.json composer.lock ./
# Install composer dependencies
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN composer install --no-dev --optimize-autoloader --no-interaction
# Copy the rest of the application code
COPY . .
# Generate application key
RUN php artisan key:generate --ansi
# Expose port
EXPOSE 80
# Start PHP-FPM and Nginx
CMD php-fpm && nginx -g "daemon off;"
Example docker-compose.yml
(for local development)
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:80"
volumes:
- .:/var/www/html
environment:
DB_CONNECTION: mysql
DB_HOST: db
DB_PORT: 3306
DB_DATABASE: laravel
DB_USERNAME: root
DB_PASSWORD: password
db:
image: mysql:8.0
environment:
MYSQL_DATABASE: laravel
MYSQL_ROOT_PASSWORD: password
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
---
Step 2: Setting up GitHub Actions Workflow
Create a .github/workflows/main.yml
file in your repository. This file defines the CI/CD workflow.
Basic CI Workflow (Testing and Building)
name: Laravel CI/CD
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
extensions: pdo_mysql, mbstring, dom, filter, gd, curl, json, iconv, imagick, zip
ini-values: post_max_size=256M, upload_max_filesize=256M
tools: composer, phpunit
- name: Install Composer Dependencies
run: composer install --no-dev --prefer-dist --no-interaction
- name: Create .env file
run: cp .env.example .env
- name: Generate Application Key
run: php artisan key:generate
- name: Run Migrations
run: php artisan migrate --force --seed --env=testing
- name: Run PHPUnit Tests
run: vendor/bin/phpunit
- name: Build Docker Image
run: docker build -t your-dockerhub-username/your-laravel-app:latest .
# Replace with your Docker Hub username and app name
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Push Docker Image
run: docker push your-dockerhub-username/your-laravel-app:latest
Explanation:
on: push
andpull_request
: Triggers the workflow on pushes tomain
and all pull requests targetingmain
.build-and-test
job:Checkout code
: Fetches your repository.Set up PHP
: Configures the PHP environment with necessary extensions and tools.Install Composer Dependencies
: Installs your project's PHP dependencies..env
andkey:generate
: Essential Laravel setup.Run Migrations
: Migrates your database schema for testing. For actual deployments, you'll run this on the server.Run PHPUnit Tests
: Executes your application's tests.Build Docker Image
: Creates a Docker image of your application.Log in to Docker Hub
: Authenticates with Docker Hub using secrets (set these in your GitHub repository settings).Push Docker Image
: Pushes the built image to your Docker Hub repository.
---
Step 3: Deployment Strategy (Example: SSH to VPS)
For deployment, you can extend your GitHub Actions workflow to connect to a server (e.g., a VPS) and pull the latest Docker image.
Add Deployment Step
# ... (previous build-and-test job)
deploy:
needs: build-and-test # This job depends on the success of build-and-test
runs-on: ubuntu-latest
environment:
name: production # Good practice to define environments
steps:
- name: Deploy to Server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/your-laravel-app # Your application directory on the server
docker pull your-dockerhub-username/your-laravel-app:latest
docker-compose down # Stop existing containers
docker-compose up -d # Start new containers
docker system prune -f # Clean up old Docker images
php artisan migrate --force # Run migrations on production
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
# Add any other post-deployment commands like queue restarts
Important Secrets:
DOCKER_USERNAME
: Your Docker Hub username.DOCKER_PASSWORD
: Your Docker Hub password or access token.SSH_HOST
: IP address or hostname of your server.SSH_USERNAME
: SSH username for your server.SSH_PRIVATE_KEY
: Your SSH private key (ensure it's secured and does not have a passphrase in CI).
How to set up secrets in GitHub:
Go to your GitHub repository -> Settings -> Security -> Secrets and variables -> Actions -> New repository secret.
---
Step 4: Environment Management
.env
files: Have different.env
files for local, testing, and production environments. Never commit sensitive production credentials to your repository.- GitHub Secrets: Use GitHub Secrets for sensitive information like database credentials, API keys, and SSH private keys.
- Docker Environment Variables: Pass environment variables to your Docker containers.
---
Database Migrations
- CI: Run
php artisan migrate --force --seed --env=testing
to prepare the database for tests. - CD (Production): Run
php artisan migrate --force
after deploying new code to apply schema changes. Consider using a deployment user with minimal database privileges.
---
Rollback Strategy
While not explicitly covered, a good CI/CD pipeline includes a rollback strategy.
- Docker Tagging: Instead of always using
latest
, tag your Docker images with Git commit SHAs or version numbers. This allows you to easilydocker pull
an older, stable version if a deployment fails. - Blue/Green or Canary Deployments: For more complex setups, explore advanced deployment strategies.
---
Conclusion
By implementing a CI/CD pipeline with GitHub Actions and Docker, you can significantly streamline your Laravel development and deployment process. This automation leads to more reliable code, faster releases, and greater confidence in your application's stability. Continuously refine your pipeline as your project evolves, adding more automated checks and deployment safeguards.