For teams deploying Laravel apps to shared hosting using Git push, post-receive hooks, and manual cPanel setups, this SOP enables:
- Clean deployment via Git
- MySQL database configuration
- Environment setup via
.env
- Seamless handling of subdomains vs. root domains
- Production-ready deployment without root access
š¦ Assumptions
- Shared hosting (e.g., A2, Hostinger, Bluehost) with cPanel + SSH
- Git, Composer, and PHP (>= 7.4) are available
- Laravel version 8+
- DNS already configured to hosting server
š SSH Key Setup (One-Time)
- Generate SSH key on your local machine:
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
- Copy the public key:
cat ~/.ssh/id_rsa.pub
- SSH into the server and append it to
~/.ssh/authorized_keys
:
nano ~/.ssh/authorized_keys # Paste the key and save
š§ Laravel Deployment Structure
ā Subdomain vs. Primary Domain: Key Differences
Context | Laravel Folder Placement | Document Root Target | Notes |
---|---|---|---|
Subdomain | Full Laravel app (including /public ) | /subdomain.domain.com/public | Clean separation; recommended for staging/UAT |
Primary Domain | Laravel core outside /public_html | /public_html/ | Must expose only public/ , rest stays outside |
š Subdomain Deployment (Recommended)
š¹ Step-by-Step
-
Create Subdomain in cPanel
- Navigate to
Domains > Subdomains
- Example:
uat.domain.com
- Set Document Root:
/home/<username>/subdomains/uat.domain.com/public
- Navigate to
Create Bare Git Repo on Server
mkdir -p /home/<username>/repos/laravel-uat.git cd /home/<username>/repos/laravel-uat.git git init --bare
- Configure
post-receive
Hook
nano hooks/post-receive
#!/bin/sh GIT_WORK_TREE=/home/<username>/subdomains/uat.domain.com git checkout -f cd /home/<username>/subdomains/uat.domain.com composer install --no-dev php artisan migrate --force php artisan config:cache php artisan route:cache php artisan view:cache
chmod +x hooks/post-receive
- Deploy from Local or GitHub
git remote add live ssh://<username>@<host_ip>/home/<username>/repos/laravel-uat.git git push live uat git push live master
š Primary Domain Deployment (domain.com
)
ā Caveat
Shared hosting ties domain.com
to /public_html
. You cannot place the full Laravel folder in /public_html
. Use the approach below:
š¹ Directory Structure
/home/<username>/laravel_main /home/<username>/public_html ā contains only files from `/public`
š¹ Steps
Push Full Laravel App to
~/laravel_main
Copy
/public
Contents to/public_html
cp -R laravel_main/public/* public_html/
- Edit
public_html/index.php
Update paths:
require __DIR__.'/../laravel_main/vendor/autoload.php'; $app = require_once __DIR__.'/../laravel_main/bootstrap/app.php';
- Install Dependencies & Migrate
cd ~/laravel_main composer install --no-dev php artisan migrate --force php artisan key:generate php artisan config:cache
- Set Permissions
chmod -R 775 storage chmod -R 775 bootstrap/cache
š ļø MySQL Database Setup
- Go to cPanel > MySQL Databases
- Create database:
db_laravel
- Create user:
user_laravel
- Assign user to database with All Privileges
- Update
.env
:
DB_CONNECTION=mysql DB_HOST=localhost DB_PORT=3306 DB_DATABASE=db_laravel DB_USERNAME=user_laravel DB_PASSWORD=StrongPassword123
ā ļø Common Issues & Fixes
Problem | Cause | Fix |
---|---|---|
500 Internal Server Error | Wrong index paths / file permissions | Fix index.php , run chmod -R 775 storage bootstrap/cache |
Composer not installed | Shared host restriction | Install locally and upload vendor/ |
.env missing | Laravel config errors | Upload .env , run php artisan config:cache |
Directory listing visible | Wrong document root | Set root to public/ , not project root |
Artisan fails | PHP CLI outdated | Check php -v , request upgrade or use compatible Laravel version |
š Summary Table
Context | Code Location | Public Files Location | Method |
---|---|---|---|
Subdomain | ~/subdomains/app/ | ~/subdomains/app/public | Full Laravel folder used |
Primary Domain | ~/laravel_main | ~/public_html | public folder only exposed |
ā Post-Deployment Checklist
- [ ]
.env
configured correctly - [ ]
php artisan key:generate
run - [ ]
storage
andbootstrap/cache
are writable - [ ] Composer dependencies installed
- [ ] Artisan commands run:
config:cache
,route:cache
,view:cache
- [ ] Database connection tested
- [ ] Subdomain or root domain resolving properly
- [ ] Debug mode off in production (
APP_DEBUG=false
)
P.S. - This post was refined with the help of ChatGPT and includes an image sourced from the web. If anyone has concerns or would like the content removed, Iām open to doing so.
Top comments (0)