Deploying Laravel 5.4 project in shared-hosting with no SSH

Despite lack of compact guides in the internet, deploying Laravel 5.4 project in cheap shared hosting (with no SSH support) is actually quite simple. The steps are:

1.  Import database to the shared hosting’s database using available tool such as phpMyAdmin

2. Upload the content of public directory in your Laravel project to public area of your shared hosting eg. ~/public_html

3. Upload your Laravel project (minus public directory) to another directory in shared hosting eg. ~/myproject. You can further exclude another directories/files that aren’t needed for production (see tips below)

4. Adjust the configuration (database connection, email server, api key .etc) in ~/myproject/.env

5. Adjust ~/public_html/index.php to match your Laravel project path:

require __DIR__.’/../bootstrap/autoload.php’; 
... 
$app = require_once __DIR__.’/../bootstrap/app.php’;

into:

require __DIR__.’/../myproject/bootstrap/autoload.php’; 
... 
$app = require_once __DIR__.’/../myproject/bootstrap/app.php’;

Well, at least that is in the nutshell

Here are some troubleshootings that I also needed to do during my first full Laravel 5.4 project deployment in shared hosting. I hope it may be useful:

Archiving directories and exporting SQL file for shared hosting

Since I need to deploy to production many times, I decided to create a simple shell script to speed up the process. It generates 3 files:

  1. MySQL dump database.sql
  2. Project directory archive app-shared.zip
  3. Public directory archive public-shared.zip
#!/bin/bash    

dbname="myproject"
dbuser="myuser"
dbpass="mypass"

#recreate database to reset auto-increment indexes
mysqladmin -u"$dbuser" -p"$dbpass" --force=TRUE drop "$dbname"
mysqladmin -u"$dbuser" -p"$dbpass" --force=TRUE create "$dbname"

#migrate and seed db    
php artisan migrate:fresh --seed

#export sql    
mysqldump -u"$dbuser" -p"$dbpass" "$dbname" -r database.sql

#zip app and public directories
zip -r app-shared.zip . -x .* -x *.zip -x *.xml -x *.sql -x *.sh -x .git/* -x public/* -x database/* -x tests/* -x webpack.mix.js -x package.json
cd public
zip -r ../public-shared.zip . -x index.php -x .htaccess -x *.json
cd ..

Running queue/jobs processor in shared hosting

If you don’t have SSH support, you can run php artisan queue:work using this simple cron job:

* * * * * /usr/bin/php -d register_argc_argv=On /home/user/laravel/artisan queue:work --once --timeout=30 --tries=2 >> /path/to/queue.log 2>&1 

It will process an entry in the queue once every minute. IMO it’s the safest setup for a cheap shared hosting.

Creating symbolic link for storage in shared hosting

If you follow the shared hosting deployment steps above, most likely you won’t get any problem at the first time. But when you try to upload files through your web application, you will get 404 error for the new files. That is because symbolic link in ~/public directory is replaced by copied directory from storage/app/public. To fix this, you need to replace ~/public/storage with symbolic link to ~/myproject/storage/app/public using this cron job below (assuming you don’t have SSH support):

* * * * * ln -s /home/user/laravel/storage/app/public /home/user/public_html/storage 

Fix import index length error on MariaDB (on common shared hosting)

If you dump your MySQL table using command such as mysqldump -u[user] -p[password] [dbname] -r [output file] and import it to MariaDB on shared hosting, most likely you’ll hit:

[PDOException] SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes 

To fix it, add one line (and use statement) in \App\Providers\AppServiceProvider:

use Illuminate\Support\Facades\Schema; 

class AppServiceProvider extends ServiceProvider { 

    public function boot() { 
        // fix issue with migration to MariaDB 
        Schema::defaultStringLength(191); 
    } 

    ... 
}

Get path to disk storage’s template
This is not specific for shared hosting but I’ll just put it here anyway.

For instance, if our storage’s template is app, calling storage_path will return a path missing the app. To overcome this, use this instead:

$filepath = Storage::disk('app')->getDriver()->getAdapter()->applyPathPrefix('category/file.ext');

or if it’s a default template:

$filepath = Storage::getDriver()->getAdapter()->applyPathPrefix('category/file.ext'); 

References

Leave a Reply

Your email address will not be published. Required fields are marked *