Deploy a Laravel App to Hostinger Shared Hosting with Bitbucket Pipelines (2026)

Deploy Laravel to Hostinger shared hosting with Bitbucket Pipelines

This guide shows you how to deploy a Laravel app to Hostinger shared hosting with Bitbucket Pipelines — automatically, on every push to main. No FTP, no manual composer install over SSH each time. Laravel adds two wrinkles a static site doesn’t have: it needs Composer dependencies built, and its public/ folder has to line up with the host’s public_html. We’ll handle both.

The shape of the pipeline

Shared hosting has PHP but no reliable Composer, no root, and tight resource limits — so we do the heavy lifting in CI: Bitbucket installs dependencies and builds assets, then ships the result to Hostinger over SSH. On every push, Pipelines runs composer install (and an asset build if you use Vite), then rsyncs the project to your account and runs a few post-deploy Artisan commands.

Why build in CI instead of on the server?

It’s tempting to just git pull and composer install over SSH on the server itself. Avoid it. Shared hosts cap memory and CPU, so a composer install on a fresh deploy often dies with an out-of-memory error halfway through — leaving your vendor/ folder in a broken, half-installed state while the site is live. Building in Bitbucket’s container sidesteps every one of those limits: full memory, a clean environment each run, and a build that either fully succeeds before anything is shipped or fails without touching production. You also get a record of every deploy and the ability to roll back by re-running an older pipeline. The server’s only job becomes receiving finished files and running migrations — work it can handle comfortably.

Prerequisites

  • A Hostinger plan with SSH — see how to SSH into Hostinger (remember, port 65002).
  • A deploy SSH key added to Hostinger, with the private half stored in Bitbucket.
  • Your Laravel app in a Bitbucket repo, with a production .env already placed on the server (never commit secrets).

Step 1 — Solve the public/ vs public_html problem first

Laravel serves from public/, but Hostinger serves your domain from public_html. The cleanest fix is to upload the app outside the web root and point the domain’s document root at the app’s public folder (Hostinger lets you set the document root per domain in hPanel). If you can’t, use the index.php path patch. We cover all three approaches in detail in the Laravel public_html guide — decide your layout before automating, because it determines your deploy target path.

For this walkthrough we’ll deploy the whole app to ~/laravel-app and assume the domain’s document root points at ~/laravel-app/public.

Step 2 — Store connection details in Bitbucket

Add the deploy key under Repository settings → Pipelines → SSH keys and add your server (with the port, e.g. your-ip:65002) under Known hosts. Then add repository variables:

VariableExample
SSH_USERu123456789
SSH_HOST123.45.67.89
REMOTE_PATH/home/u123456789/laravel-app/

Step 3 — The bitbucket-pipelines.yml

Commit this to the repo root. It builds on a PHP image, ships the files, then runs post-deploy commands over SSH:

image: php:8.3-cli

pipelines:
  branches:
    main:
      - step:
          name: Build (composer)
          caches:
            - composer
          script:
            - apt-get update && apt-get install -y unzip git
            - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
            - composer install --no-dev --optimize-autoloader --no-interaction
          artifacts:
            - vendor/**
      - step:
          name: Deploy to Hostinger
          deployment: production
          script:
            - pipe: atlassian/rsync-deploy:1.3.0
              variables:
                USER: $SSH_USER
                SERVER: $SSH_HOST
                REMOTE_PATH: $REMOTE_PATH
                LOCAL_PATH: '.'
                SSH_PORT: '65002'
                EXTRA_ARGS: '--exclude=.env --exclude=.git --exclude=storage --exclude=node_modules'
      - step:
          name: Post-deploy (artisan)
          script:
            - pipe: atlassian/ssh-run:0.8.1
              variables:
                SSH_USER: $SSH_USER
                SERVER: $SSH_HOST
                PORT: '65002'
                COMMAND: 'cd laravel-app && php artisan migrate --force && php artisan config:cache && php artisan route:cache'

Why it’s built this way:

  • --no-dev --optimize-autoloader keeps the production install lean and fast.
  • The excludes matter. Never overwrite the server’s .env or storage/ (user uploads, logs), and don’t ship .git.
  • artisan migrate --force is required in non-interactive (production) mode; config:cache and route:cache speed up the live app.
  • Separate steps mean a failed migration doesn’t leave a half-copied site — the deploy step has already completed atomically via rsync.

Step 4 — Handle assets (if you use Vite)

If your app compiles front-end assets, add a Node build before the deploy step (or use a multi-tool image). The simplest approach is a build sub-step:

      - step:
          name: Build assets
          image: node:20
          caches:
            - node
          script:
            - npm ci
            - npm run build
          artifacts:
            - public/build/**

The compiled public/build then rides along in the rsync deploy.

Step 5 — Push and verify

git add bitbucket-pipelines.yml
git commit -m "Automate Laravel deploy to Hostinger"
git push origin main

Watch the three steps go green in Bitbucket’s Pipelines view, then load your domain. From now on, every push to main rebuilds dependencies and redeploys.

Troubleshooting

  • 500 error after deploy. Usually a stale config cache or a missing .env/APP_KEY on the server. SSH in and run php artisan config:clear, then confirm .env exists.
  • “Permission denied” on deploy. Deploy key not on Hostinger, or Bitbucket using the wrong key. Re-check your Hostinger SSH keys.
  • Connection refused. Port must be 65002 in both the rsync and ssh-run pipes.
  • Site loads the wrong folder. Your document root isn’t pointing at public/ — revisit Step 1.
  • Migrations hang. Add --force (production is non-interactive) and make sure DB credentials in the server .env are correct.

Deploying an Angular front-end instead? The static-build version is in our guide on deploying Angular to Hostinger with Bitbucket Pipelines.

Frequently asked questions

Can I deploy Laravel to shared hosting with CI/CD?

Yes. Build dependencies in Bitbucket Pipelines, rsync the app to your Hostinger account over SSH, then run artisan migrate --force and cache commands as a post-deploy step — no root or Docker needed.

How do I run artisan migrate during deployment?

Add a post-deploy step that SSHes into the server (using the atlassian/ssh-run pipe on port 65002) and runs php artisan migrate --force. The --force flag is required because production runs non-interactively.

Where do I put the .env file?

Place the production .env on the server once, and exclude it from the rsync (--exclude=.env) so deploys never overwrite it. Never commit secrets to the repository.

Previous