Deploying to Shared Linux Hosting
A step-by-step guide for cPanel-style shared hosting (GoDaddy, Hostinger, Bluehost, Namecheap, etc.) where you may or may not have SSH access.
Requirements
- PHP 8.2+ (select this in cPanel → MultiPHP Manager)
- MySQL 5.7+ / MariaDB 10.3+
- PHP extensions:
pdo_mysql, mbstring, openssl, tokenizer, xml, ctype, json, bcmath, fileinfo, gd(cPanel's Select PHP Version screen lets you tick these) - Composer — either via SSH, or run it locally and upload the
vendor/folder (see below)
1. Prepare the App Locally
Shared hosts rarely let Composer download packages reliably (timeouts, memory limits), so it's most reliable to build vendor/ on your own machine first and upload everything together.
# On your own computer, inside the project folder
composer install --no-dev --optimize-autoloader
cp .env.example .env
php artisan key:generate
# Zip everything except node_modules (there isn't one) and .git
zip -r school-management.zip . -x ".git/*"
You should now have a zip containing app/, vendor/, public/, .env, and everything else.
2. Create the Database
- cPanel → MySQL Databases → create a database (e.g.
cpaneluser_school) - Create a database user with a strong password, and add it to the database with "All Privileges"
- Note the full database name, username, and password — cPanel prefixes them with your account name
3. Upload the Files
Laravel's entry point is public/index.php, but shared hosting normally serves your domain straight from public_html/ — so the app's other folders (app/, vendor/, etc.) can't sit inside public_html/ directly for security. Two common approaches:
Option A — You can change the document root (recommended)
Some hosts let you set a subdomain's or the main domain's document root to any folder (cPanel → Domains → edit). If so:
- Upload the whole project to a folder outside
public_html, e.g./home/cpaneluser/school-management/ - Set the domain/subdomain's document root to
/home/cpaneluser/school-management/public - Done — Laravel is now served correctly with everything else hidden from the web
Option B — Document root is locked to public_html
If your host won't let you change the document root:
- Upload the project to
/home/cpaneluser/school-management/(outsidepublic_html) - Copy the contents of
school-management/public/intopublic_html/(index.php, .htaccess, etc.) - Edit the uploaded
public_html/index.phpand fix the two require paths to point at your actual app folder:
require __DIR__.'/../school-management/vendor/autoload.php';
$app = require_once __DIR__.'/../school-management/bootstrap/app.php';
Adjust the relative path if your folder structure differs. This keeps app/, .env, etc. outside the publicly-served folder while still working.
Uploading: use cPanel File Manager's "Upload" + "Extract" on your zip (fastest for one big file), or FTP/SFTP with FileZilla for incremental changes later.
4. Configure .env
Edit .env via File Manager (or SFTP) with your real values:
APP_NAME="Your School Name"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://yourdomain.com
DB_CONNECTION=mysql
DB_HOST=localhost
DB_DATABASE=cpaneluser_school
DB_USERNAME=cpaneluser_dbuser
DB_PASSWORD=your-db-password
SESSION_DRIVER=database
CACHE_STORE=database
QUEUE_CONNECTION=database
MAIL_MAILER=smtp
MAIL_HOST=mail.yourdomain.com
MAIL_PORT=587
MAIL_USERNAME=notifications@yourdomain.com
MAIL_PASSWORD=your-mailbox-password
MAIL_ENCRYPTION=tls
APP_DEBUG=false on a live site — leaving it true exposes stack traces (including config values) to visitors on error pages.Also set ACTIVATION_KEY to a long random secret — the site won't serve any page to visitors until this key is entered once on the /activate screen:
# Generate a random key locally, then paste it into ACTIVATION_KEY in .env
php -r "echo bin2hex(random_bytes(20));"
5. Run Migrations & Seed
cd ~/school-management
php artisan migrate --seed --force
--force is required because APP_ENV=production normally asks for confirmation.
Check cPanel for a Terminal icon first — many hosts include one even without full SSH. If truly unavailable, create a temporary one-time script:
<?php
// public/run-migrations.php — DELETE THIS FILE IMMEDIATELY AFTER USE
require __DIR__.'/../vendor/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$kernel->call('migrate', ['--force' => true, '--seed' => true]);
echo $kernel->output();
Upload it into public/, visit https://yourdomain.com/run-migrations.php once, confirm it printed success, then delete the file immediately — leaving it live lets anyone re-run your migrations.
6. Storage Link (for file uploads to display)
Uploaded photos, logos, and study material files live in storage/app/public and need a symlink at public/storage to be reachable by the browser.
php artisan storage:link
Some shared hosts disable PHP's symlink() function entirely (check cPanel → Select PHP Version → Extensions, or ask support). If storage:link isn't possible:
- Use the same temporary-script trick as above, calling
$kernel->call('storage:link'), then delete it — or - If
symlink()is disabled outright, manually copy the folder instead: copy everything fromstorage/app/public/into a realpublic/storage/directory, and repeat that copy after any new upload (not automatic — only use this as a last resort).
7. File Permissions
Laravel needs to write to a few folders. Via File Manager, select each and use "Change Permissions" (or chmod over SSH):
chmod -R 755 storage bootstrap/cache
If you still get "Permission denied" writing to logs/cache, try 775, and confirm the folder owner matches your hosting account's PHP user (check with your host if unsure).
8. Cron Job (Task Scheduler)
cPanel → Cron Jobs → add a new job running every minute:
* * * * * cd /home/cpaneluser/school-management && php artisan schedule:run >> /dev/null 2>&1
Not strictly required for this app today (no scheduled jobs are defined yet), but it's standard practice so any future scheduled tasks work automatically.
9. Enable HTTPS
cPanel → SSL/TLS Status → run AutoSSL for your domain (usually free and automatic on most hosts). Once active, make sure APP_URL in .env uses https:// so generated links are correct.
Troubleshooting
- 500 error, blank page: temporarily set
APP_DEBUG=trueto see the real error, then set it back tofalseonce fixed. - "could not find driver" (PDO): the
pdo_mysqlextension isn't enabled — turn it on in cPanel's PHP extension manager. - "exec() has been disabled": some hosts disable
exec/proc_open/symlinkfor security. Composer's post-install scripts andstorage:linkmay need the manual workarounds above. - Styling missing / raw HTML: double check the document root actually points at the
public/folder (Option A) or thatpublic_html/index.php's paths were corrected (Option B). - "open_basedir restriction in effect": your host has locked PHP to specific folders — contact support to whitelist your app's path, or move the app inside the allowed base directory.
- Emails not sending: confirm your SMTP mailbox credentials under Settings → Email Carrier match cPanel's mail settings, and that port 587/465 isn't blocked by the host (some require their own outgoing mail relay).
Back to the User Guide.