Deploy Laravel w/ Vite Application using 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!"
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.
- Go to your repository settings.
- Navigate to the "Secrets" section.
- 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!