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.

Two paths below: most steps have an With SSH version (faster, run via Terminal) and a No SSH version (cPanel File Manager + a one-time script) — use whichever your host supports. Many shared hosts now include a Terminal app in cPanel even on basic plans, so check there first.

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

  1. cPanel → MySQL Databases → create a database (e.g. cpaneluser_school)
  2. Create a database user with a strong password, and add it to the database with "All Privileges"
  3. 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:

  1. Upload the whole project to a folder outside public_html, e.g. /home/cpaneluser/school-management/
  2. Set the domain/subdomain's document root to /home/cpaneluser/school-management/public
  3. 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:

  1. Upload the project to /home/cpaneluser/school-management/ (outside public_html)
  2. Copy the contents of school-management/public/ into public_html/ (index.php, .htaccess, etc.)
  3. Edit the uploaded public_html/index.php and 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
Always set 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

With SSH
cd ~/school-management
php artisan migrate --seed --force

--force is required because APP_ENV=production normally asks for confirmation.

No SSH

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.

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=true to see the real error, then set it back to false once fixed.
  • "could not find driver" (PDO): the pdo_mysql extension isn't enabled — turn it on in cPanel's PHP extension manager.
  • "exec() has been disabled": some hosts disable exec/proc_open/symlink for security. Composer's post-install scripts and storage:link may need the manual workarounds above.
  • Styling missing / raw HTML: double check the document root actually points at the public/ folder (Option A) or that public_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.