Guide PHP & Development

How to deploy a Laravel application

Point the document root at Laravel's public folder, set up .env, run migrations, and cache the config — what a Laravel deploy needs.

Updated 9 min read Intermediate

Laravel expects a specific folder layout: one folder, public, is meant to be reachable by a browser, and everything else — application code, configuration, the .env file, the vendor folder — is meant to sit outside it. Getting that one arrangement right is most of what makes a Laravel deployment different from dropping plain PHP files into a folder.

Document root: the part that trips people up

If your hosting lets you set a custom document root per domain, point it at the project's public folder rather than the project root:

/home/youraccount/laravel-app/          <- project root, NOT public
/home/youraccount/laravel-app/public/   <- point the document root here
If you cannot set a custom document root

Some shared hosting only lets you serve from a fixed folder such as public_html. In that case, upload the Laravel project one level above public_html, then move only the contents of the project's public folder into public_html, and edit public_html/index.php to point its require paths at the actual location of vendor/autoload.php and bootstrap/app.php one directory up. This keeps application code and .env outside the web-reachable folder even when the hosting itself does not offer a configurable document root.

Getting the application onto the server

Two common routes, both covered in more detail elsewhere in this section:

  • Deploy from a Git repository, pulling the latest commit onto the server and running the remaining steps below afterwards.
  • Sync files with rsync, excluding .env, vendor and node_modules from the transfer so they are managed separately per environment rather than overwritten from your local copy.

Installing dependencies

Over SSH, in the project root — not public — run Composer with production flags:

composer install --no-dev --optimize-autoloader

--no-dev skips packages only needed for local development and testing, and --optimize-autoloader builds a faster class map for production. See using Composer on shared hosting if you have not installed Composer into the account yet.

The .env file

Laravel reads its configuration from a .env file in the project root, and this file should never be committed to version control or copied unchanged between environments — it holds database credentials and the application encryption key, and production needs its own values, not the ones from local development.

APP_ENV=production
APP_DEBUG=false
APP_KEY=base64:generate-this-with-artisan
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=yourdatabase
DB_USERNAME=yourdbuser
DB_PASSWORD=yourdbpassword

Generate a fresh application key rather than reusing one from another environment:

php artisan key:generate
APP_DEBUG must be false in production

With debug mode on, an unhandled exception shows a full stack trace, file paths and configuration values directly to visitors. Confirm APP_DEBUG=false before the site goes live — this is one of the most common accidental information leaks on a freshly deployed Laravel site.

See setting environment variables for more on how .env works and alternatives where your control panel offers its own environment variable screen instead.

Database and cache setup

  1. Run the migrations

    php artisan migrate --force applies the schema. The --force flag is required to run migrations when APP_ENV is set to production, as a deliberate guard against running them by accident.

  2. Cache the configuration

    php artisan config:cache combines every config file into one cached file, which is faster to load on every request than reading and merging them individually.

  3. Cache routes and views

    php artisan route:cache and php artisan view:cache do the same for the route table and compiled Blade templates.

Clear the cache after every subsequent deploy

If you change a config value, a route or an environment variable after caching, the cached version keeps being served until you clear it: php artisan config:clear (and the matching route:clear, view:clear) followed by re-running the cache commands above. A change that appears to have no effect after a deploy is very often this.

File permissions

Laravel needs to write to two folders — storage and bootstrap/cache — for logs, compiled views and cached files. If either is not writable by the PHP process, the application throws permission errors on almost every page. On shared hosting this is usually a matter of confirming ownership matches your account rather than opening permissions further than necessary.

Scheduled tasks and queues

If the application uses Laravel's scheduler, a single cron entry runs everything it defines — see scheduling a PHP script for the cron syntax involved:

* * * * * php /home/youraccount/laravel-app/artisan schedule:run >> /dev/null 2>&1

Queues that rely on a persistent worker process are generally a poor fit for shared hosting, since there is no long-running process to keep a worker alive between requests — that is a good reason to consider a VPS for an application that depends heavily on queued jobs.

Related reading