Skip to main content
    Back to all articles

    Laravel CI/CD Pipeline: From Development to Production

    Backend
    14 min read
    By Bahaj abderrazak
    Featured image for "Laravel CI/CD Pipeline: From Development to Production"

    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 and pull_request: Triggers the workflow on pushes to main and all pull requests targeting main.
    • 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 and key: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 easily docker 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.

    Tags

    Laravel
    CI/CD
    GitHub Actions
    Docker
    DevOps
    Deployment