Skip to main content
    Back to all articles

    Laravel CI/CD Pipeline: From Development to Production

    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.

    \n

    ---

    \n

    Why CI/CD for Laravel?

    \n

    Continuous Integration (CI) and Continuous Delivery/Deployment (CD) automate critical steps in the software development lifecycle. For Laravel projects, this translates to:

    \n
      \n
    • Faster Feedback Loops: Catch errors early with automated tests.
    • \n
    • Consistent Environments: Docker ensures your application behaves the same from development to production.
    • \n
    • Reduced Manual Errors: Automate repetitive deployment tasks.
    • \n
    • Increased Confidence: Deploy with assurance knowing your code has passed all checks.
    • \n
    \n

    ---

    \n

    Core Components of Our Pipeline

    \n

    We''ll focus on these key technologies:

    \n
      \n
    • GitHub Actions: Our CI/CD orchestrator.
    • \n
    • Docker: For containerizing the Laravel application and its dependencies.
    • \n
    • PHPUnit: Laravel''s built-in testing framework.
    • \n
    • Composer: PHP dependency management.
    • \n
    • Artisan: Laravel''s command-line interface.
    • \n
    \n

    ---

    \n

    Step 1: Project Setup and Dockerization

    \n

    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.

    \n

    Example Dockerfile

    \n
          # For example, using an official PHP-FPM image\n      FROM php:8.2-fpm-alpine\n\n      # Install system dependencies\n      RUN apk add --no-cache \\\n          nginx \\\n          mysql-client \\\n          nodejs \\\n          npm\n\n      # Install PHP extensions\n      RUN docker-php-ext-install pdo_mysql opcache\n\n      # Set working directory\n      WORKDIR /var/www/html\n\n      # Copy composer.lock and composer.json\n      COPY composer.json composer.lock ./\n\n      # Install composer dependencies\n      RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer\n      RUN composer install --no-dev --optimize-autoloader --no-interaction\n\n      # Copy the rest of the application code\n      COPY . .\n\n      # Generate application key\n      RUN php artisan key:generate --ansi\n\n      # Expose port\n      EXPOSE 80\n\n      # Start PHP-FPM and Nginx\n      CMD php-fpm && nginx -g "daemon off;"
    \n

    Example docker-compose.yml (for local development)

    \n
          version: '3.8'\n\n      services:\n        app:\n          build:\n            context: .\n            dockerfile: Dockerfile\n          ports:\n            - "8000:80"\n          volumes:\n            - .:/var/www/html\n          environment:\n            DB_CONNECTION: mysql\n            DB_HOST: db\n            DB_PORT: 3306\n            DB_DATABASE: laravel\n            DB_USERNAME: root\n            DB_PASSWORD: password\n\n        db:\n          image: mysql:8.0\n          environment:\n            MYSQL_DATABASE: laravel\n            MYSQL_ROOT_PASSWORD: password\n          ports:\n            - "3306:3306"\n          volumes:\n            - db_data:/var/lib/mysql\n\n      volumes:\n        db_data:
    \n

    ---

    \n

    Step 2: Setting up GitHub Actions Workflow

    \n

    Create a .github/workflows/main.yml file in your repository. This file defines the CI/CD workflow.

    \n

    Basic CI Workflow (Testing and Building)

    \n
          name: Laravel CI/CD\n\n      on:\n        push:\n          branches:\n            - main\n        pull_request:\n          branches:\n            - main\n\n      jobs:\n        build-and-test:\n          runs-on: ubuntu-latest\n          steps:\n            - name: Checkout code\n              uses: actions/checkout@v3\n\n            - name: Set up PHP\n              uses: shivammathur/setup-php@v2\n              with:\n                php-version: '8.2'\n                extensions: pdo_mysql, mbstring, dom, filter, gd, curl, json, iconv, imagick, zip\n                ini-values: post_max_size=256M, upload_max_filesize=256M\n                tools: composer, phpunit\n\n            - name: Install Composer Dependencies\n              run: composer install --no-dev --prefer-dist --no-interaction\n\n            - name: Create .env file\n              run: cp .env.example .env\n\n            - name: Generate Application Key\n              run: php artisan key:generate\n\n            - name: Run Migrations\n              run: php artisan migrate --force --seed --env=testing\n\n            - name: Run PHPUnit Tests\n              run: vendor/bin/phpunit\n\n            - name: Build Docker Image\n              run: docker build -t your-dockerhub-username/your-laravel-app:latest .\n              # Replace with your Docker Hub username and app name\n\n            - name: Log in to Docker Hub\n              uses: docker/login-action@v2\n              with:\n                username: ${{ secrets.DOCKER_USERNAME }}\n                password: ${{ secrets.DOCKER_PASSWORD }}\n\n            - name: Push Docker Image\n              run: docker push your-dockerhub-username/your-laravel-app:latest
    \n

    Explanation:

    \n
      \n
    • on: push and pull_request: Triggers the workflow on pushes to main and all pull requests targeting main.
    • \n
    • build-and-test job:
    • \n
    • Checkout code: Fetches your repository.
    • \n
    • Set up PHP: Configures the PHP environment with necessary extensions and tools.
    • \n
    • Install Composer Dependencies: Installs your project''s PHP dependencies.
    • \n
    • .env and key:generate: Essential Laravel setup.
    • \n
    • Run Migrations: Migrates your database schema for testing. For actual deployments, you''ll run this on the server.
    • \n
    • Run PHPUnit Tests: Executes your application''s tests.
    • \n
    • Build Docker Image: Creates a Docker image of your application.
    • \n
    • Log in to Docker Hub: Authenticates with Docker Hub using secrets (set these in your GitHub repository settings).
    • \n
    • Push Docker Image: Pushes the built image to your Docker Hub repository.
    • \n
    \n

    ---

    \n

    Step 3: Deployment Strategy (Example: SSH to VPS)

    \n

    For deployment, you can extend your GitHub Actions workflow to connect to a server (e.g., a VPS) and pull the latest Docker image.

    \n

    Add Deployment Step

    \n
          # ... (previous build-and-test job)\n\n        deploy:\n          needs: build-and-test # This job depends on the success of build-and-test\n          runs-on: ubuntu-latest\n          environment:\n            name: production # Good practice to define environments\n          steps:\n            - name: Deploy to Server\n              uses: appleboy/ssh-action@master\n              with:\n                host: ${{ secrets.SSH_HOST }}\n                username: ${{ secrets.SSH_USERNAME }}\n                key: ${{ secrets.SSH_PRIVATE_KEY }}\n                script: |\n                  cd /var/www/your-laravel-app # Your application directory on the server\n                  docker pull your-dockerhub-username/your-laravel-app:latest\n                  docker-compose down # Stop existing containers\n                  docker-compose up -d # Start new containers\n                  docker system prune -f # Clean up old Docker images\n                  php artisan migrate --force # Run migrations on production\n                  php artisan cache:clear\n                  php artisan config:clear\n                  php artisan route:clear\n                  php artisan view:clear\n                  # Add any other post-deployment commands like queue restarts
    \n

    Important Secrets:

    \n
      \n
    • DOCKER_USERNAME: Your Docker Hub username.
    • \n
    • DOCKER_PASSWORD: Your Docker Hub password or access token.
    • \n
    • SSH_HOST: IP address or hostname of your server.
    • \n
    • SSH_USERNAME: SSH username for your server.
    • \n
    • SSH_PRIVATE_KEY: Your SSH private key (ensure it''s secured and does not have a passphrase in CI).
    • \n
    \n

    How to set up secrets in GitHub:

    \n

    Go to your GitHub repository -> Settings -> Security -> Secrets and variables -> Actions -> New repository secret.

    \n

    ---

    \n

    Step 4: Environment Management

    \n
      \n
    • .env files: Have different .env files for local, testing, and production environments. Never commit sensitive production credentials to your repository.
    • \n
    • GitHub Secrets: Use GitHub Secrets for sensitive information like database credentials, API keys, and SSH private keys.
    • \n
    • Docker Environment Variables: Pass environment variables to your Docker containers.
    • \n
    \n

    ---

    \n

    Database Migrations

    \n
      \n
    • CI: Run php artisan migrate --force --seed --env=testing to prepare the database for tests.
    • \n
    • CD (Production): Run php artisan migrate --force after deploying new code to apply schema changes. Consider using a deployment user with minimal database privileges.
    • \n
    \n

    ---

    \n

    Rollback Strategy

    \n

    While not explicitly covered, a good CI/CD pipeline includes a rollback strategy.

    \n
      \n
    • 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.
    • \n
    • Blue/Green or Canary Deployments: For more complex setups, explore advanced deployment strategies.
    • \n
    \n

    ---

    \n

    Conclusion

    \n

    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.