Shared hosting with cPanel is still a popular choice for many developers — it’s affordable and easy to manage. The catch? Deploying is usually a manual process: upload files via FTP or File Manager one by one, then run migrations yourself.
With GitHub Actions, you can automate all of that. Every time you push to main, your server pulls the latest code, installs dependencies, builds assets, and runs migrations — all hands-free.
Also check out my previous article: How to Deploy Laravel to cPanel Hosting
TL;DR
- Clone your repo straight into the
public_htmlfolder - Add an
.htaccessat the project root to redirect traffic topublic/ - Install Node.js via NVM in cPanel Terminal
- Generate an SSH key on the server, store the private key in GitHub Secrets
- Create
.github/workflows/deploy.yml, push, and you’re done
Step 1: Clone Your Project into public_html
Open cPanel → Terminal and clone your repo directly into public_html. Since public_html is your domain’s default web root, we’ll use it as the Laravel project root.
|
|
Tip: Back up anything important inside
public_htmlfirst before wiping it withrm -rf.
Step 1.1: Extra Setup for Private Repos
If your repo is private, cloning via HTTPS will prompt for authentication. The cleanest way to handle this on the server is with an SSH deploy key.
Generate an SSH key in cPanel Terminal:
|
|
Hit Enter when asked for a passphrase (leave it empty).
Display the public key:
|
|
Copy the entire output.
Add it to GitHub:
- Go to your repo on GitHub → Settings → Deploy keys
- Click Add deploy key
- Fill in the Title (e.g.,
cpanel-production) - Paste the public key into the Key field
- Check Allow write access if needed (usually not for deployment)
- Click Add key
Set up SSH config so Git knows which key to use:
Create ~/.ssh/config:
|
|
|
|
Now clone using the SSH URL instead of HTTPS:
|
|
Tip: You’ll find the SSH URL on your GitHub repo page — click the Code button, then the SSH tab.
Step 2: Set Up .htaccess to Redirect to public/
Laravel serves requests from the public/ subfolder, not the project root. Create an .htaccess file at the root of public_html so all traffic gets routed there — without /public/ showing up in the URL.
~/public_html/.htaccess:
|
|
Step 3: Install Dependencies and Configure .env
Install Composer dependencies, set up your .env file, generate the application key, and run migrations.
|
|
Fill in the database section of .env with your cPanel credentials. A small gotcha I’ve hit before: database names and usernames in cPanel always include your cPanel account prefix (e.g., myaccount_laraveldb).
|
|
Step 4: Install Node.js via NVM
Since we’re using Vite to build assets, we need Node.js on the server. The safest way on shared hosting is through NVM.
|
|
That version is the latest at the time of writing. For the most recent version, check the nvm-sh/nvm docs.
After installation, load NVM into your shell and install Node.js LTS:
|
|
To make sure NVM stays loaded when GitHub Actions connects via SSH, add those same lines to ~/.bashrc and source ~/.bashrc inside ~/.bash_profile.
Step 5: Generate an SSH Key and Store It in GitHub Secrets
GitHub Actions needs SSH access to your server. Generate a key pair in cPanel Terminal:
|
|
If you already created a deploy key in Step 1.1, you can reuse that one instead of generating a new pair.
Display the private key and copy the entire thing — including the -----BEGIN----- and -----END----- lines:
|
|
Go to your GitHub repo → Settings → Secrets and variables → Actions and add these five secrets:
| Secret | Value |
|---|---|
SSH_HOST |
Your server’s IP or hostname |
SSH_USERNAME |
Your cPanel username |
SSH_KEY |
The private key from the step above |
SSH_PORT |
22 |
PROJECT_PATH |
/home/cpaneluser/public_html |
Step 6: Create the GitHub Actions Workflow
In your local project, create the workflow file:
|
|
.github/workflows/deploy.yml:
|
|
A quick breakdown of what the workflow does on every push to main:
- Pulls the latest code
- Installs Composer and NPM dependencies
- Builds front-end assets with Vite
- Runs database migrations
- Clears and recaches Laravel’s config, routes, views, and events
The workflow_dispatch trigger means you can also run it manually from the Actions tab whenever you need to.
Step 7: Push and Verify
Commit the workflow and push to main:
|
|
This push itself will trigger your very first deployment. Head over to the Actions tab on GitHub and watch each step run — logs appear in real time.
If every step comes back green, open your domain and verify everything’s working. From now on, git push origin main = automatic deploy to your server.
Quick Troubleshooting
Here are a few issues I’ve run into with this setup:
-
npm: command not found— NVM isn’t being loaded in the SSH session. Add theexport NVM_DIRandsourcelines at the top of thescript:block in your workflow. -
Permission denied (publickey)— Double-checkchmod 700 ~/.sshandchmod 600 ~/.ssh/authorized_keyson the server. -
404 or directory listing — Your
.htaccessin~/public_htmlis either missing or incorrect. Revisit Step 2. -
Migration fails — Check the database name and username prefix in your
.envfile. Remember, cPanel prepends your account name to both.
If you hit any other snags, drop a comment or reach out — I’m happy to help :)
This setup takes a bit of work up front, but after that your deploy workflow is clean as can be. No more manual uploads, no more forgetting to run migrations. Everything runs automatically every time you push.
Happy coding!