How do I deploy my Laravel applications using Github Action?
This is my second article on Medium. In my first one, I talked about cPanel because I deploy 90% of my applications on cPanel. It's not because cPanels are cheap or easy. It is because I can only afford it instead of VPS, AWS, or similar things.
But, instead of just using git push
and git pull
, I complicate the deployment (or make it easy) using automation pipelines. I have a ready-to-use Github Action script which you can just Ctrl+C
and Ctrl+V.
You can do the same for your Laravel projects. I will go step-by-step about both the YAML
and bash
scripts that I use to deploy my applications.
The YAML File
In your project folder, create a .github/workflows
folder. This is where GitHub detects the automation scripts. Here, I create the YAML files named after the branch name which I plan to deploy. It helps to maintain the pipeline for each branch. But for this project, I will simply work on master
branch.
So, the files to be created are:
.github/workflows/master.yml
deploy.sh
(in the project folder)
Add the following variables to your repository secrets/variables (I prefer secrets):
MASTER_SSH_HOST
: Hostname of the serverMASTER_SSH_USERNAME
: SSH username of the serverMASTER_SSH_KEY
: SSH private key (Append the public key inside~/.ssh/authorized_keys
file of the destination server)MASTER_SSH_PORT
: SSH portMASTER_SSH_PATH
: Path to the application folder in the server (Must be pointing to the directory containingpublic
folder)
The Jobs
Build
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Copy .env
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
- name: Directory Permissions
run: chmod -R 777 storage bootstrap/cache
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '19.x'
- run: npm install
- run: npm run build
- name: Commit built assets
run: |
git config --local user.email "achyutkneupane@gmail.com"
git config --local user.name "Achyut Neupane"
git checkout -B deploy
git add -f public/
git commit -m "Build front-end assets"
git push -f origin deploy
The above YAML job has the following steps:
- The GitHub action runner checks out the latest codebase from the repository.
- It copies the
.env.example
file to.env
if.env
file does not exist. - It changes the permissions of
storage
andbootstrap/cache
directories to777
(not good from a security POV though) - It sets up node version
19.x
. - It installs the npm packages and builds the front-end assets.
- Any content inside the public folder, especially the
build
folder, is added to.gitignore
file. So, we force add the public folder and commit the changes to a new decoy branchdeploy
. - The branch is then pushed to the repository.
Deploy
deploy:
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to Master
uses: appleboy/ssh-action@master
with:
username: ${{ secrets.MASTER_SSH_USERNAME }}
host: ${{ secrets.MASTER_SSH_HOST }}
key: ${{ secrets.MASTER_SSH_KEY }}
port: ${{ secrets.MASTER_SSH_PORT }}
script: |
cd ${{ secrets.MASTER_SSH_PATH }}
chmod +x ./deploy.sh
./deploy.sh
The above YAML job has the following steps:
- The
build
job is needed inneeds
to ensure that the deployment is done only after thebuild
job is successful. - The GitHub action runner accesses the hosting server using SSH.
- The variables are required to smoothly access the server.
- The script is executed in order:
- Change the directory to the application folder. It must be the directory containingpublic
folder.
- Change the permission ofdeploy.sh
file to executable.
- Execute thedeploy.sh
file.
Delete
In this step, we will delete the decoy branch. This branch was created as a decoy which carries the build
contents which we usually don’t push into GitHub.
delete:
name: Delete deploy branch
runs-on: ubuntu-latest
needs: deploy
steps:
- name: Checkout branch
uses: actions/checkout@v4
- name: Delete deploy branch
run: |
git config --local user.email "achyutkneupane@gmail.com"
git config --local user.name "Achyut Neupane"
git push origin --delete deploy
The above YAML job has the following steps:
- The
deploy
job is needed inneeds
to ensure that the branch is deleted only after the deployment is successful. - The GitHub action runner checks out the latest codebase from the repository.
- The deploy branch is deleted from the repository. This is to keep the repository clean.
Full Code
name: Deploy to Master
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Copy .env
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
- name: Directory Permissions
run: chmod -R 777 storage bootstrap/cache
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '19.x'
- run: npm install
- run: npm run build
- name: Commit built assets
run: |
git config --local user.email "achyutkneupane@gmail.com"
git config --local user.name "Achyut Neupane"
git checkout -B deploy
git add -f public/
git commit -m "Build front-end assets"
git push -f origin deploy
deploy:
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to Master
uses: appleboy/ssh-action@master
with:
username: ${{ secrets.MASTER_SSH_USERNAME }}
host: ${{ secrets.MASTER_SSH_HOST }}
key: ${{ secrets.MASTER_SSH_KEY }}
port: ${{ secrets.MASTER_SSH_PORT }}
script: |
cd ${{ secrets.MASTER_SSH_PATH }}
chmod +x ./deploy.sh
./deploy.sh
delete:
name: Delete deploy branch
runs-on: ubuntu-latest
needs: deploy
steps:
- name: Checkout branch
uses: actions/checkout@v4
- name: Delete deploy branch
run: |
git config --local user.email "achyutkneupane@gmail.com"
git config --local user.name "Achyut Neupane"
git push origin --delete deploy
The Bash File
This bash file, which we create inside the project’s root folder, will run inside the server. It will run basic commands that we manually type in our terminal.
#!/bin/sh
set -e
echo "Deploying application ..."
# Enter maintenance mode
(php artisan down --secret="bypass") || true
# Update codebase
git fetch origin deploy
git reset --hard origin/deploy
# Install dependencies based on lock file
composer install --no-interaction --prefer-dist --optimize-autoloader
# Migrate database
php artisan migrate --force
# Clear cache
php artisan optimize:clear
# Exit maintenance mode
php artisan up
echo "Application deployed!"
The bash script is straight to the point. It activates the maintenance mode before running the commands in order. Once all the commands are executed, it deactivates the maintenance mode.
FAQs
- Why the decoy branch?
- Not all cPanels have Node installed. We can install it but it won’t always be the same. So, I wrote the decoy branch script to runnpm run build
inside the GitHub Action and forcefully push thebuild
folder to the destination server. That’s why thedelete
job is important to clean up the repository after deployment is successful. - What if I have
node
accessible in my cPanel?
- Skip thebuild
anddelete
jobs above. Adjust the bash file and just run thedeploy
job. It will save resources and time for you.
I tried my best to handle the automation in low-end cPanels. I too sometimes face issues with these scripts. But I have initialized these scripts for so many projects and I have been able to solve many of the common issues that arise with this process.
If you face one, feel free to comment or mail me at achyutkneupane@gmail.com.