How to deploy a Laravel app
Laravel runs as ordinary PHP, so it works on both cPanel and DirectAdmin hosting. The one thing that trips people up is that Laravel expects the web server to point at its public folder, and shared hosting points at public_html. Pick one of the three layouts below and the rest is routine.
Before you start
- PHP version: set it to what your Laravel version needs (Laravel 10 and 11 need PHP 8.1 or newer). See How to change my PHP version and PHP settings.
- PHP extensions: Laravel needs
mbstring,openssl,pdo_mysql,tokenizer,xml,ctype,json,bcmath,fileinfo. Most are on by default. See How to enable PHP extensions (imagick, intl, redis and more). - Database: create a MySQL database and user. See How to create a MySQL database and use phpMyAdmin.
- SSH makes this much easier (Composer, artisan). It's off by default on shared hosting; open a support ticket to have it enabled. See How to get SSH access and connect. Everything below has a no-SSH alternative.
Step 1: Choose a folder layout
Option A: App outside public_html (recommended)
Only Laravel's public folder is reachable from the web. Your .env, code and storage are not.
-
Upload the whole project to a folder in your home directory, for example
/home/username/laravel, except thepublicfolder. -
Upload the contents of
public(index.php, .htaccess, assets) intopublic_html. Delete the placeholderindex.htmlfirst. -
Edit
public_html/index.phpand change the paths that go up one level:require __DIR__.'/../laravel/vendor/autoload.php'; $app = require_once __DIR__.'/../laravel/bootstrap/app.php';(Laravel 11 has a
maintenanceline above these; change its path the same way.) -
If you use
php artisan storage:link, the link must point frompublic_html/storageto/home/username/laravel/storage/app/public. Over SSH:ln -s /home/username/laravel/storage/app/public /home/username/public_html/storage. Without SSH, open a ticket and we'll create it.
Option B: Point a subdomain or addon domain at public
cPanel lets you choose the document root when creating a domain.
- Upload the whole project to
/home/username/laravel. - In cPanel go to Domains → Domains → Create A New Domain. Enter the domain or subdomain, untick Share document root, and set the Document Root to
laravel/public. - Done. No file edits needed.
On DirectAdmin, use Option A or C, or open a support ticket.
Option C: Rewrite from public_html to public
Quickest for the main domain if you can't change its document root, but the whole project sits inside public_html.
-
Upload the whole project into
public_html. -
Create
public_html/.htaccesscontaining:RewriteEngine On RewriteCond %{REQUEST_URI} !^/public/ RewriteRule ^(.*)$ /public/$1 [L] -
Laravel's own
public/.htaccesshandles the rest. Option A is still the safer layout.
Step 2: Install dependencies
With SSH:
cd ~/laravel
composer install --no-dev --optimize-autoloader
If composer isn't found, download it into the folder: curl -sS https://getcomposer.org/installer | php then use php composer.phar install --no-dev.
Without SSH: run composer install --no-dev on your computer, then upload the resulting vendor folder with the project. Zip it first; it's thousands of files.
Step 3: Configure .env
-
Copy
.env.exampleto.env(or upload your own). -
Set at least:
APP_ENV=production APP_DEBUG=false APP_URL=https://example.co.za DB_CONNECTION=mysql DB_HOST=localhost DB_DATABASE=username_dbname DB_USERNAME=username_dbuser DB_PASSWORD=yourpassword -
APP_KEY: over SSH run
php artisan key:generate. Without SSH, run it on your computer and copy theAPP_KEYline across. -
Mail: use your Webway mailbox with
MAIL_MAILER=smtp,MAIL_HOST=mail.example.co.za,MAIL_PORT=465,MAIL_ENCRYPTION=ssl, and the full address and mailbox password. See Email server settings: IMAP, POP3 and SMTP. -
Queues: set
QUEUE_CONNECTION=syncunless you set up the cron approach below. Always-runningqueue:workprocesses aren't supported on shared hosting. -
Sessions and cache: use the
fileordatabasedrivers.
Never leave APP_DEBUG=true on a live site. It prints your database password in error pages.
Step 4: Permissions
Files should be 644 and folders 755. PHP runs as your own user, so storage and bootstrap/cache are already writable. Don't use 777; it can cause a 500 error.
Step 5: Run artisan commands
With SSH, from the project folder:
php artisan migrate --force
php artisan storage:link
php artisan config:cache
php artisan route:cache
php artisan view:cache
Without SSH:
- Migrations: import a SQL dump of your local database in phpMyAdmin instead.
- Caches: skip them. Laravel works without cached config; it's just a little slower.
- storage:link: open a ticket and ask for the link.
Step 6: Scheduler and queues with cron
Laravel's scheduler needs a cron entry every minute. See How to set up a cron job.
* * * * * cd /home/username/laravel && /usr/local/bin/php artisan schedule:run >> /dev/null 2>&1
For queued jobs without a permanent worker, run the queue from cron and let it exit when empty:
* * * * * cd /home/username/laravel && /usr/local/bin/php artisan queue:work --stop-when-empty --max-time=50 >> /dev/null 2>&1
Step 7: Test and secure
- Open the site. Check the padlock and force HTTPS. See How to force my website to use HTTPS.
- Confirm
https://example.co.za/.envreturns a 404 or 403, never the file contents. - Check
storage/logs/laravel.logfor errors after the first visits.
Common problems
| Problem | Cause | Fix |
|---|---|---|
| 500 error, blank page | vendor missing, wrong PHP version, 777 permissions, or an .htaccess typo |
Check storage/logs/laravel.log and the panel's error log. Reset permissions to 644/755 |
| "No application encryption key has been specified" | APP_KEY empty |
Step 3 |
| "SQLSTATE[HY000] [1045] Access denied" | Database user not added to the database, or wrong password | Add the user to the database with all privileges |
| Home page works, other routes 404 | public/.htaccess missing, or Option C rewrite not in place |
Re-upload public/.htaccess |
Images under /storage 404 |
Link missing | Step 1 / Step 5 |
| Scheduled tasks never run | Cron missing or wrong PHP path | Step 6 |
Still stuck?
Open a support ticket and include:
- The domain and which layout (A, B or C) you used.
- The Laravel and PHP versions.
- The last 20 lines of
storage/logs/laravel.log. - The last 20 lines of the panel's error log.
- Whether you have SSH enabled and whether
composer installcompleted.
Did this answer it?