Deploy Laravel w/ Vite Application using CI/CD

· 3min · deftmartian
Laravel logo plus CI/CD

Overview

A CI/CD (Continuous Integration/Continuous Deployment) pipeline automates the process of building, testing, and deploying your application. For a Laravel application using Vite for asset bundling, this pipeline will handle building assets and copying them to the web server and then deployment steps on the web server. This pipeline will trigger every time code is pushed to or pulled from the production branch.

Requirements

  • A Laravel project with Vite configured.
  • Git configured and a GitHub repository for your project.
  • SSH access to your production server.
  • A new SSH key pair for GitHub Actions.
  • Composer installed on your production server.

Step-by-Step Guide

1. Create a Deployment Script

SSH into your production server, navigate to your project root directory and create a deployment script named deploy.sh

cd /path/to/your/laravel/project
touch deploy.sh && chmod +x deploy.sh
nano deploy.sh

The deployment script will automate the following tasks:

  • Enter maintenance mode.
  • Reset the project to the latest commit from the production branch.
  • Pull the latest code.
  • Set file permissions for production.
  • Install Composer dependencies.
  • Run database migrations.
  • Rebuild cached files for optimal performance.
  • Restart Apache/Nginx web server.
  • Exit maintenance mode.

Here's an example of what the deploy.sh script might look like:

#!/bin/bash

# Exit on error
set -e

echo "Deployment started ..."

# Enter maintenance mode (or proceed if already down)
(php artisan down) || true

# Reset project
git reset --hard
git clean -f

# Pull the latest code from the 'production' branch
git pull origin production

# Set file permissions
sudo chown $USER:$USER -R .
sudo chown $USER:www-data -R storage bootstrap/cache
sudo find . -type d -exec chmod 755 {} \;
sudo find . -type f -exec chmod 644 {} \;
sudo chmod -R 775 storage bootstrap/cache
sudo chmod 600 .env

# Clear compiled files
php artisan clear-compiled
php artisan optimize:clear

# Install Composer dependencies
composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader

# Run database migrations
php artisan migrate --force

# Recreate cache for performance
php artisan optimize

# Restart Apache/Nginx
sudo systemctl restart apache2 # or nginx depending on your setup

# Exit maintenance mode
php artisan up

echo "Deployment finished!"
tip
tip

Verify that www-data is the correct web server user. You can check this by running ps aux | grep apache (or nginx)

2. Test the Deployment

Before automating everything, test the deployment script manually on your server to ensure it works without issues.

sh deploy.sh

If any errors occur, fix them before proceeding.

3. Set Up Secrets in GitHub

For security, store your SSH private key and other sensitive information as secrets in GitHub Actions.

  1. Go to your repository settings.
  2. Navigate to the "Secrets" section.
  3. Add the following secrets:
    • SSH_PRIVATE_KEY: Your SSH private key. This should be a dedicated key - don't use a key you use elsewhere.
    • HOST: The hostname or IP address of your production server.
    • USERNAME: The username used for SSH access.
    • PORT: The SSH port number (default is 22).

4. Set Up GitHub Actions Workflow

Create a workflow configuration file in your repository to automate the deployment process.

cd /path/to/your/laravel/project
mkdir -p .github/workflows
nano .github/workflows/deploy-production.yml

This GitHub action workflow will complete the following tasks.

  • Trigger: Executes on a push or pull of the production branch
  • Environment: Sets up asset build environment.
  • Builds Assets: Runs Vite's build process to create optimized static assets.
  • SCP Action: Copies built assets securely to your production server using SSH.
  • SSH Action: Executes the deployment script on your server, which handles the rest of the deployment process.

Here's an example of what the deploy-production.yml might look like:

name: Deploy Production

on:
  push:
    branches:
      - production
  pull_request:
    branches:
      - production

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 'latest'

      - name: Install npm dependencies
        run: npm install

      - name: Build assets
        run: npm run build

      - name: Copy built files to server
        uses: appleboy/scp-action@master
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          port: ${{ secrets.PORT }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          source: public/build/**
          target: /path/to/your/laravel/project/public/build/
          strip_components: 2 #test and adjust accordingly
          rm: true  #remove existing files first

      - name: Deploy to server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          port: ${{ secrets.PORT }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /path/to/your/laravel/project/
            ./deploy.sh

5. Test the Action

Push a change to GitHub. Open up GitHub Actions, and view the latest workflow run. You should be able to see it working through the steps. In case of error, the GitHub project owner will receive an email.

Wrapping up

This pipeline provides a solid foundation to customise for your use case. Remember, test thoroughly. I recommend first setting this up on a testing branch. If you have any suggestions for improvement, please let me know in the comments!