Published
06 Apr, 2025
Category
DevOps / Hosting
From Localhost to Live: My Laravel Deploy on Hostinger

A step-by-step guide to deploying a Laravel app on Hostinger shared hosting. Covers folder structure, PHP/Composer setup, Vite manifest fixes, database configuration, storage links, troubleshooting common errors, and ends with a one-screen deploy checklist.

1. Intro (Set the stage)
  • Hook: Deploying Laravel on shared hosting isn’t as straightforward as clicking publish. I learned this the hard way on Hostinger, so here’s my real-world step-by-step playbook.
  • Promise: what the reader will get (deployment flow, quirks, fixes, troubleshooting, checklist).

2. Why Hostinger + Laravel (when shared hosting is enough vs VPS)
  • If your app is a brochure site, a portfolio, a small SaaS MVP, or an internal tool with predictable traffic and no long-running workers, shared hosting is fine (and cheap). You get an SSL, SFTP/SSH, PHP selector, MySQL, and automatic backups out of the box. The trade-off is that you don’t control the server: no root access, limited CLI tooling, and process managers (for queues/websockets) are constrained.

  • Go shared hosting when:

    • You can pre-build assets locally (no Node needed on the server).
    • Queues are light or can be run via schedule:run/cron (not 24/7 workers).
    • You don’t need websockets, Horizon, Octane, or custom extensions.
  • Go VPS/Forge/Docker when:

    • You need always-on queue workers / websockets.
    • You want full control over PHP extensions, Nginx/Apache, and system deps.
    • You’re planning CI/CD, zero-downtime deploys, or horizontal scaling.
  • My rule of thumb: if you’re fighting the platform (crons, workers, memory), you’re already paying the “dev time tax.” That’s when a VPS pays for itself.

3. Final server layout (public_html vs laravel/)
  • Most shared hosts force your web root to public_html/. A safe layout is:

    /home/USERNAME/
    ├── laravel/                # your Laravel project root (private)
    │   ├── app/
    │   ├── bootstrap/
    │   ├── config/
    │   ├── public/             # original Laravel /public (we won’t expose this directly)
    │   └── ...
    └── public_html/            # the actual web root
        ├── index.php           # points back to ../laravel/*
        ├── .htaccess
        └── build/              # symlink to ../laravel/public/build
    
  • You do not point the domain directly at laravel/public. Instead, keep all app code outside the web root, then place only the web-facing files in public_html/.

  • Update public_html/index.php so it references your app correctly:

    make(Illuminate\Contracts\Http\Kernel::class);
    $response = $kernel->handle(
        $request = Illuminate\Http\Request::capture()
    )->send();
    $kernel->terminate($request, $response);
    
  • Permissions to sanity-check:

    chmod -R ug+rwx storage bootstrap/cache
    
4. Composer 2 + PHP 8.3 on shared hosting
  • On Hostinger, you can target a specific PHP version by calling its full path. I used PHP 8.3:

    /opt/alt/php83/usr/bin/php -v
    
  • If composer isn’t globally available, install a local copy once:

    cd ~/laravel
    /opt/alt/php83/usr/bin/php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
    /opt/alt/php83/usr/bin/php composer-setup.php --install-dir=. --filename=composer.phar
    rm composer-setup.php
    
  • Then install dependencies with sane flags:

    cd ~/laravel
    /opt/alt/php83/usr/bin/php -d memory_limit=1024M composer.phar install \
      --no-dev --prefer-dist --optimize-autoloader
    
  • Run Laravel optimizations (once your .env exists):

    /opt/alt/php83/usr/bin/php artisan key:generate --force
    /opt/alt/php83/usr/bin/php artisan config:cache
    /opt/alt/php83/usr/bin/php artisan route:cache
    /opt/alt/php83/usr/bin/php artisan view:cache
    
  • Tip: If composer install keeps failing due to memory, run it locally, zip vendor/, upload, and unzip on the server.

5. Upload strategy (SFTP: what goes in public_html vs project root)
  • Goal: keep your app private; only expose the web root.

  • Steps:

    1. Create folders:
    mkdir -p /home/USERNAME/laravel
    # public_html already exists
    
    2. Upload project via SFTP into /home/USERNAME/laravel/
       - Skip node_modules and .git
       - Skip vendor/ if you’ll run composer remotely
    
    3. Populate public_html/:
       - Copy only the contents of laravel/public/ once
       - Or create fresh index.php and .htaccess
       - Never put .env here (.env stays in /home/USERNAME/laravel/.env)
    
    4. Build assets locally:
    npm ci
    npm run build
    Upload laravel/public/build
    
    5. Symlink build folder:
    ln -s /home/USERNAME/laravel/public/build /home/USERNAME/public_html/build
    
  • Checklist: App code → /laravel, Only index.php, .htaccess, and build symlink → /public_html.