πŸš€ Deploy Laravel App Using GitHub Actions

it's very nice to build a CI/CD for your app after you finish developing or for staging to check your project on a server, so we get the best way to do that.

it's very nice to build a CI/CD for your app after you finish developing or for staging to check your project on a server, so we get the best way to do that.

you can follow this lesson's steps to get your app live.

🏯 Install Cloudpanel

to start working on the new server you need to install a lot of packages, or use a docker container, the easy way to start deploying a Laravel php app is to get a VPS from any service provider like AWS EC2 or Hetziner Cloud ...etc, after you got your VPS and it must be Ubuntu 22.01 you can follow this lesson.

What is Cloudpanel

CloudPanel is a free and modern server control panel to configure and manage a server with an obsessive focus on simplicity.

Run PHP, Node.js, Static Websites, Reverse Proxies, and Python Applications in no time on a High-Performance Technology Stack.

Quick launch support for:

Benefits

  • Free
  • Easy to use
  • Community Driven
  • Blazing Fast Page Loads - Up to 250x faster
  • Secure (free SSL/TLS certificates)
  • Cloudflare integration
  • High Performance
  • Ready to go within 1 minute
  • Supports all major clouds
  • Support for X86 and ARM

Install Cloudpanel

login to your server using your terminal with SSH

ssh root@yourIpAddress

If you are using a password to log in, the SSH command would be:

ssh -i path_to_your_private_key root@yourIpAddress

If you are using a private key to log in, the SSH command would be:

Login via SSH to the Server.

now you are on the server root and the server is empty, to install the panel we need some packages to be installed first and the system to be updated.

apt update && apt -y upgrade && apt -y install curl wget sudo

This command will update the system packages, and add the required packages.

now you can start installing the Cloudpanel

curl -sS https://installer.cloudpanel.io/ce/v2/install.sh -o install.sh; \
echo "3c30168958264ced81ca9b58dbc55b4d28585d9066b9da085f2b130ae91c50f6 install.sh" | \
sha256sum -c && sudo bash install.sh

Access CloudPanel

SECURITY

For security reasons, access CloudPanel as fast as possible to create the admin user. There is a small time window where bots can create the user. If possible, open port 8443 only for your IP via the firewall.

You can now access CloudPanel via Browser: https://yourIpAddress:8443

Ignore the self-signed certificate warning click on Advanced and Proceed to continue to CloudPanel.

πŸ”“ Link Cloudflare

we believe that the security and cache are better for any app, so we need to secure our server by using Cloudflare DNS reverser and its power of caching.

Create Cloudflare account

it's easy to create a new Cloudflare account like any website out to this link and create a new account

Add your domain to Cloudflare

to add your domain to Cloudflare, on the dashboard of Cloudflare you will find the Websites tab you can click on it and click on the right button to Add a site

after that I will ask you to add your domain and give you a namespace to change it on your domain then go to your domain service provider and change the namespace to the Cloudflare namespace, this process can take 24 hours for some providers, I use Google Domains it takes just 10min, and now your domain is ready on the Cloudflare.

Connect Domain to Cloudpanel

now get to your domain after activating it, you will find the DNS tab click on it and add a new A Record with your server IP make sure that your Proxy status is DNS only and that can be pointed to cp.

now go to your Cloudpanel click on Admin Area and then select Settings and change CloudPanel Custom Domain to cp.YOUR_DOMAIN

it will generate auto SSL for you and link the domain for you, you can access the Cloudpanel using your domain

Connect Project Domain

to connect your project domain you need to add a new website, from the Website tab click on the Add Site button, select Create PHP Site App, fill in the details, and click on Create.

now Cloudpanel will redirect and add an nginx template and PHP-FPM to point your domain, go to Cloudflare and add a new DNS A Record and point it to your server domain, then go to SSL/TLS tab and create a new Origin Server SSL, it will give you a 15 years SSL for free, copy it and go to Cloudpanel and select your new Website app and from the tab of SSL/TLS Click on actions and then Import Certificate and add the Cloudflare SSL.

now your domain is linked and secured by SSL, you can access your domain with SSL and make sure that the domain is working.

πŸ”ƒ Clone Repo

login to your server using your terminal with SSH with the user of a created project

ssh youproject@yourIpAddress

If you are using a password to log in, the SSH command would be:

ssh -i path_to_your_private_key root@yourIpAddress

If you are using a private key to log in, the SSH command would be:

Login via SSH to the Server.

you are on the server user and you can generate an SSH key to link it with your GitHub Project to generate this key use this command line

ssh-keygen -t ed25519 -C "[email protected]"

and you can just click enter after finishing you will get a path ending with .pub copy the path and then use this command to view the path SSH key content and then copy it

cat .ssh/rsa.pub

where.ssh/rsa.pub is the path of your key

now go to your GitHun Repo and on the Settings Tab, select Deploy keys and then add deploy Key and then select a name add your SSH key then save it.

now your server has access to this repo and you can clone it so go back to your server and then on the path of the project it can look like this

cd /home/yourproject/htdocs/yourproject.com

and clone your repo like this

git clone [email protected]:tomatophp/tomato.git .

it will clone the project on the current directory

now it's time to install the composer packages and set up the project .env file follow these steps

Copy .env file

use this command to copy .env.example

cp .env.example .env

now update your details and create a new database from Cloudpanel on the Database Tab create a new database, then add the details of the database to .env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=tomato-kit
DB_USERNAME=root
DB_PASSWORD=26111995

change the main URL in .env file

APP_URL=https://yourproject.com
APP_HOST=yourproject.com

Install Composer packages and clean the project

to install composer packages just use this command

composer install

then you can run your project command and the important one is

php artisan key:generate
php artisan config:cache
php artisan storage:link
php artisan optimize:clear

now your app is ready for Frontend assets to make this work you need to install npm

Install NVM/NPM/Yarn

Install nvm with the following command:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash

Update the current shell environment:

source ~/.bashrc

Install your required Node.js version e.g. 18:

nvm install 18

Activate the installed Node.js version:

nvm use 18

Done! Check the Node.js version:

node -v

now you can install Yarn using this command

npm -g i yarn

now you can easily run yarn to build assets

yarn & yarn build

your project is ready now and you can view it on your browser.

πŸŽ‡ Workflow

the next step to build your CI/CD is to create a Workflow yaml file on your project repo, on your project file create a new folder with the name .github inside this folder create another one with the name workflows In this folder create a file with the name ci.yaml

mkdir .github
cd .github
mkdir workflows
cd workflows
touch ci.yaml

now on your yaml file add this script

name: Testing Laravel with MySQL
on:
  pull_request:
    branches:
      - master
  push:
    branches:
      - master

jobs:
  laravel:
    name: Laravel (PHP ${{ matrix.php-versions }})
    runs-on: ubuntu-latest
    env:
      DB_DATABASE: laravel
      DB_USERNAME: root
      DB_PASSWORD: password
      BROADCAST_DRIVER: log
      CACHE_DRIVER: redis
      QUEUE_CONNECTION: redis
      SESSION_DRIVER: redis
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ALLOW_EMPTY_PASSWORD: false
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: laravel
        ports:
          - 3306/tcp
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

      redis:
        image: redis
        ports:
          - 6379/tcp
        options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3
    strategy:
      fail-fast: false
      matrix:
        php-versions: ['8.2']
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php-versions }}
          extensions: sqlite, pdo_sqlite, pcntl, zip, intl, exif, mbstring, dom, fileinfo, mysql
          coverage: xdebug

      - name: Get composer cache directory
        id: composer-cache
        run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

      - name: Cache composer dependencies
        uses: actions/cache@v3
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          # Use composer.json for key, if composer.lock is not committed.
          # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: ${{ runner.os }}-composer-


      - name: Install Composer dependencies
        run: composer install --no-progress --prefer-dist --optimize-autoloader

      - name: Prepare Laravel Application
        run: |
          php -r "file_exists('.env') || copy('.env.example', '.env');"
          php artisan key:generate

      - name: Clear Config
        run: php artisan config:clear

      - name: Run Migration
        run: php artisan migrate -v
        env:
          DB_PORT: ${{ job.services.mysql.ports['3306'] }}
          REDIS_PORT: ${{ job.services.redis.ports['6379'] }}

      - name: Test with phpunit
        run: vendor/bin/phpunit --coverage-text
        env:
          DB_PORT: ${{ job.services.mysql.ports['3306'] }}
          REDIS_PORT: ${{ job.services.redis.ports['6379'] }}

      - name: Install Yarn dependencies
        run: yarn

      - name: Compile assets
        run: yarn build

      - name: Deploy to server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          port: ${{ secrets.SSH_PORT }}
          password: ${{ secrets.SSH_PASSWORD }}
          script: cd ${{ secrets.SSH_PATH }} && ./.scripts/deploy.sh

this script takes fire when any push comes to master branch, and after testing everything on your app it will run the Script using SSH on your server by using details on the server on GitHub Secrets to change these details go to your GitHub repo and then the Settings tab, and then select Actions secrets and variables and then add a new secret with key SSH_HOST with your server IP, and you must add details like this

SSH_HOST: 8.8.8.8
SSH_USERNAME: yourproject
SSH_PORT: 22
SSH_PASSWORD: yourpassword
SSH_PATH: /home/yourproject/htdocs/yourdomain.com

then make sure you change these details on .env.example

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=password

now it's time to create a Deploy Script on the next step

πŸ¦• Deploy Script

the deploy script is the script that runs every time you make a push, so you must add a selected command when it runs every time you make a push.

to add a script make a folder with the name .scripts and then create a file deploy.sh and make sure you give the file permissions to run with 755.

mkdir .scripts
cd .scripts
touch script.sh
chmod 755 deploy.sh

now your deploy script is ready add to this file this script and you can custom it as you like

#!/bin/bash
set -e

echo "Deployment started ..."

# Enter maintenance mode or return true
# if already is in maintenance mode
(php artisan down) || true

# Pull the latest version of the app
git reset --hard
git pull origin master

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

# Clear the old cache
php artisan clear-compiled

# Recreate cache
php artisan optimize

# Compile npm assets
yarn
yarn build

# Run database migrations
php artisan migrate --force

# Exit maintenance mode
php artisan up

echo "Deployment finished!"

now push all of these updates to your GitHub repo and you will find on the Actions tab that the action is run after that, you will find the updates on your server.

Share To Social Networks