🚀 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:
- Amazon Web Services
- Digital Ocean
- Hetzner Cloud
- Google Compute Engine
- Microsoft Azure
- Oracle Cloud
- Vultr
- Other
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.