Building Web Apps with Laravel Module 1 – Start here: Laravel 13 in 2026
Install PHP, Composer and Laravel 13
Choose an install route for PHP 8.5 and Composer, create a Laravel 13 project, read what each step did, start the dev processes and fix setup errors.
What you will learn
- Choose an install route for PHP, Composer and Laravel 13 that fits your system, and name what each route needs
- Create a project with composer create-project and explain what each part of its output did
- Compare laravel new with composer create-project, and list the processes that composer run dev starts
- Check an installation with php artisan about, and fix three common setup errors from their messages
Before you start
What you’ll learn · Before you start
What you will learn
- Choose an install route for PHP, Composer and Laravel 13 that fits your system, and name what each route needs
- Create a project with composer create-project and explain what each part of its output did
- Compare laravel new with composer create-project, and list the processes that composer run dev starts
- Check an installation with php artisan about, and fix three common setup errors from their messages
Before you start
On this page
One command, composer create-project laravel/laravel pathshala, turns an empty folder into a working Laravel 13 project: 109 Composer packages, a settings file, a secret application key and a SQLite database with nine tables. The same project fails in three predictable ways on a first day, and each failure is one line to read and one command to fix.
This lesson picks an install route, runs create-project and reads its output step by step, compares it with laravel new, checks the result with php artisan about, lists the four processes behind composer run dev and reproduces the three setup errors on purpose. Pathshala, a fictional coaching-centre manager, is the project that the next lessons extend, starting with the project’s folders and configuration.
Four routes lead to PHP, Composer and Laravel 13
Laravel 13 needs PHP 8.3 or newer, and its release notes list PHP 8.3 to 8.5 as supported. The installation guide asks for three tools before the first project: PHP, Composer: PHP's dependency manager. It reads composer.json, downloads the packages a project needs from Packagist and installs them into the vendor folder. and the Laravel installer, plus Node with npm, or Bun, to build the frontend assets. The php track’s install lesson covers the PHP side in more depth. Four routes cover most computers:
- php.new. One command per system (macOS, Windows PowerShell, Linux) installs PHP, Composer and the Laravel installer together. Running the command again updates all three.
- Herd. A desktop app for macOS and Windows that bundles PHP and Nginx, with command line tools for
php,composer,laravel,nodeandnpm. - Your own PHP and Composer. If both are already installed,
composer global require laravel/installeradds the installer. A version manager, Homebrew or your Linux packages give you PHP. - Sail. Laravel’s Docker setup: a
compose.yamland asailscript that start PHP and its services in containers. It needs Docker.
This track uses the third route, with mise is a tool that installs several versions of a developer tool and runs a command with the one you name. to run PHP 8.5.11 and Composer 2.10.3 by exact version, so every recording here can be repeated with the same versions. The recordings use no containers, so Sail is described but never shown. Answer three questions about your own computer, and the table marks the routes that fit:
Answer the questions above: the table then marks what fits your case and says why the rest does not.
| Route | Runs on | What you get | An exact PHP release | What it needs first |
|---|---|---|---|---|
| php.new command What it is: one command that installs PHP, Composer and the Laravel installer | Good macOS, Windows PowerShell and Linux each have a one-line command | Good PHP, Composer and the Laravel installer; run the command again to update | Mixed the command names a branch such as 8.5, and an update brings the newest release | Good a terminal; on Windows, PowerShell opened as administrator |
| Herd app What it is: a desktop app with PHP and Nginx built in | Mixed macOS and Windows only | Good PHP, Nginx and command line tools for php, composer, laravel, node and npm | Mixed the installer downloads the latest PHP release | Good nothing but the installer; on macOS it keeps Nginx running in the background |
| Your own PHP and Composer What it is: a version manager, Homebrew or your Linux packages | Good any system that PHP runs on | Mixed you install PHP and Composer yourself, then add the Laravel installer | Good mise exec php@8.5.11 runs one named release, as this track does | Mixed a version manager, Homebrew or your distribution's packages |
| Sail What it is: the project runs in Docker containers | Mixed macOS, Linux, and Windows through WSL2 | Good a compose.yaml and a sail script that start PHP and its services in containers | Mixed the images carry their own PHP, and rebuilding them brings newer packages | Poor Docker installed and running, and a project to add Sail to |
How the answers decide
- “Which system do you develop on?” Linux: rules out Herd app. Herd is an app for macOS and Windows, so on Linux the other routes take its place.
- “Which system do you develop on?” Windows: adds a caveat to Sail. Sail runs on Windows through WSL2, a Linux system that runs inside Windows.
- “Is Docker installed and running on your computer?” No, it does not: rules out Sail. Sail starts its services as Docker containers, so it cannot run without Docker.
- “Must every computer run the same exact PHP release?” Yes, the same release: adds a caveat to php.new command and Herd app. They install the newest release, so two computers can end on different patch releases.
In short: php.new or Herd for the shortest way to a first project, your own PHP when you must name an exact release, and Sail when a team already works in Docker.
Which PHP versions Laravel 13 supports
Laravel 13 supports PHP 8.3, 8.4 and 8.5, and the skeleton’s composer.json requires php at ^8.3. A computer with PHP 8.2 does not meet that, whatever else is installed, and composer check-platform-reqs is the command that says so.
Checkpoint
Check: Four install routes
One create-project command builds the whole project
Run it first, then read it. The session below starts in an empty folder and runs the one command, and it is the longest recording of the lesson, because Composer extracts 109 packages:
"""Creates the course's project the way the Laravel docs' own fallback does it: composer create-project, offline."""
from session import run
run("""
composer create-project laravel/laravel pathshala
""", app=False) Output
$ composer create-project laravel/laravel pathshala
Creating a "laravel/laravel" project at "./pathshala"
Installing laravel/laravel (v13.10.1)
- Installing laravel/laravel (v13.10.1): Extracting archive
Created project in pathshala
> @php -r "file_exists('.env') || copy('.env.example', '.env');"
Loading composer repositories with package information
Updating dependencies
Lock file operations: 109 installs, 0 updates, 0 removals
- Locking brick/math (1.0.0)
- Locking carbonphp/carbon-doctrine-types (3.2.1)
- Locking dflydev/dot-access-data (v3.0.3)
… and 106 more lines like these
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 109 installs, 0 updates, 0 removals
- Installing doctrine/inflector (2.1.0): Extracting archive
- Installing doctrine/lexer (3.0.3): Extracting archive
- Installing dragonmantank/cron-expression (v3.6.0): Extracting archive
… and 106 more lines like these
51 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
INFO Discovering packages.
laravel/pail .......................................................... DONE
laravel/pao ........................................................... DONE
laravel/tinker ........................................................ DONE
nesbot/carbon ......................................................... DONE
nunomaduro/collision .................................................. DONE
nunomaduro/termwind ................................................... DONE
79 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> @php artisan vendor:publish --tag=laravel-assets --ansi --force
INFO No publishable resources for tag [laravel-assets].
No security vulnerability advisories found.
> @php artisan key:generate --ansi
INFO Application key set successfully.
> @php -r "file_exists('database/database.sqlite') || touch('database/database.sqlite');"
> @php artisan migrate --graceful --ansi
INFO Preparing database.
Creating migration table .............................................. DONE
INFO Running migrations.
0001_01_01_000000_create_users_table .................................. DONE
0001_01_01_000001_create_cache_table .................................. DONE
0001_01_01_000002_create_jobs_table ................................... DONE
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 create_project_session.py
Composer’s manual describes create-project as the equivalent of a git clone followed by composer install. The first package it installs, laravel/laravel (v13.10.1), is the application skeleton: the folders and files of an empty Laravel app. The line that starts with > is a script from the skeleton’s composer.json copying .env.example to .env, the environment file: The .env file in a Laravel project. It holds the values that differ from one computer to the next, such as the database settings, the debug flag and the application key, and stays out of version control. More in “Project structure, configuration and Artisan”. Then Composer works in two phases. Lock file operations: 109 installs is the first: it resolves every package the skeleton needs, development tools included, and Writing lock file saves the exact versions to lock file: composer.lock records the exact version of every package Composer resolved. composer install reads it, so every computer installs the same code.. Package operations: 109 installs is the second: it installs those same versions into the vendor folder.
The recording ran offline from a local copy of the same versions. On your computer Composer fetches them from Packagist: The default package repository for Composer. Composer asks it where to download a package such as laravel/framework., the default package repository, and the output is the same.
After the install, three more scripts finish the setup. Package discovery found six packages that register themselves with Laravel. Application key set successfully is key:generate filling in the application key: The secret stored as APP_KEY in the environment file. Laravel's encrypter uses it, php artisan key:generate creates it, and each environment has its own.. The last block creates the empty SQLite file and runs three migration: A schema change written as a PHP class with an up method that applies it and a down method that undoes it, kept in database/migrations and run in file-name order. More in “Your first CRUD feature with Eloquent”, which build eight tables for users, sessions, the cache and jobs, beside the migrations table that records them. Step through the same run:
The drawing shows the whole scene; its 8 steps are listed below.
Every step as text
- The skeleton arrives. Composer fetches the application skeleton, version 13.10.1 of laravel/laravel, and extracts it into a new folder called pathshala. It holds the folders you will work in, but no third-party code yet.
- .env is copied. A script in the skeleton's composer.json copies .env.example to .env if there is no .env. The settings file now exists, with an empty APP_KEY for the step that fills it.
- 109 packages are locked. Composer resolves the skeleton's requirements, framework 13.34.0 among them, and writes every exact version to composer.lock. The output counts 109 installs, development tools included.
- The packages are installed. Composer installs the same 109 packages from the lock file into the vendor folder, then builds its autoloader, the map that lets PHP find a class by name.
- Packages announce themselves. Laravel runs package discovery. Packages that ship a Laravel service provider, six in this run, are registered without any line of your code. They include Tinker, Pail and Carbon.
- The key is generated. php artisan key:generate writes a random value into APP_KEY in .env. Laravel's encrypter needs it, so the step runs once for each new copy of the project.
- The database file is created. The default database is SQLite, one file. An empty database/database.sqlite is created, so the first migration has somewhere to write.
- Three migrations build tables. php artisan migrate creates its own migrations table and runs the three migrations. They build eight more tables, among them users, sessions, cache and jobs, so the database holds nine. The project is ready to start.
Key idea
create-project is a clone, an install from a lock file and three setup scripts in one command, and the lock file it writes is what lets a teammate repeat the install exactly.
Checkpoint
Check: What create-project builds
laravel new asks the questions create-project skips
laravel new pathshala is a different program: the Laravel installer, put on your computer by php.new, Herd or composer global require laravel/installer. It still builds on the same skeleton, since the installer’s source runs composer create-project laravel/laravel underneath, but it asks questions first and then adjusts the project to your answers. Its source asks, in this order:
| Question | Choices | What create-project gives |
|---|---|---|
| Project name | any folder name | the name you typed in the command |
| Starter kit? | no (default) or yes | none |
| Frontend stack | Blade (default), React, Svelte, Vue or Livewire; with a kit, the last four | Blade views only |
| Authentication provider | Laravel’s own, or WorkOS (asked with a kit) | none |
| Testing framework | Pest (default) or PHPUnit | PHPUnit |
| Laravel Boost? | yes or no | not installed |
| Database | SQLite, MySQL, MariaDB, PostgreSQL, SQL Server | SQLite |
A starter kit is a ready-made set of routes, controllers and views for registering and signing in users, and the documentation says plainly that kits are optional. Boost is Laravel’s optional package that gives coding assistants facts about your project. The recordings in this track use create-project, because it needs only Composer and prints no questions, so a recorded session can repeat it. The Pathshala project therefore has the plain defaults: Blade views, PHPUnit and SQLite.
Try laravel new on your own computer
Install the installer, run laravel new pathshala-kit, and answer the questions once with a starter kit and once without. Then compare the two routes/web.php files.
Checkpoint
Check: laravel new and its questions
php artisan about shows the install is sound
Three commands answer “is this setup healthy?” in a few seconds. The version commands name the tools, php artisan about summarises the project, and composer check-platform-reqs compares the PHP on this computer with what the installed packages ask for. The session ends by printing the .env lines behind the driver names, so you can see where each one is set:
"""Checks a fresh project: the tool versions, php artisan about, the .env lines behind its drivers, the platform check and the PHP version Composer wrote into vendor."""
from session import run
run(r"""
php -r 'echo "PHP ", PHP_VERSION, "\n";'
composer --version 2>/dev/null | cut -d' ' -f1-3
php artisan --version
php artisan about
grep -E '^(DB_CONNECTION|SESSION_DRIVER|CACHE_STORE|QUEUE_CONNECTION|MAIL_MAILER)=' .env
composer check-platform-reqs
grep 'Your Composer dependencies' vendor/composer/platform_check.php
""", app=True) Output
$ php -r 'echo "PHP ", PHP_VERSION, "\n";'
PHP 8.5.11
$ composer --version 2>/dev/null | cut -d' ' -f1-3
Composer version 2.10.3
$ php artisan --version
Laravel Framework 13.34.0
$ php artisan about
Environment ................................................................
Application Name ................................................... Laravel
Laravel Version .................................................... 13.34.0
PHP Version ......................................................... 8.5.11
Composer Version .................................................... 2.10.3
Environment .......................................................... local
Debug Mode ......................................................... ENABLED
URL ......................................................... localhost:8000
Maintenance Mode ....................................................... OFF
Timezone ............................................................... UTC
Locale .................................................................. en
Cache ......................................................................
Config .......................................................... NOT CACHED
Events .......................................................... NOT CACHED
Routes .......................................................... NOT CACHED
Views ........................................................... NOT CACHED
Drivers ....................................................................
Broadcasting ........................................................... log
Cache ............................................................. database
Database ............................................................ sqlite
Logs ........................................................ stack / single
Mail ................................................................... log
Queue ............................................................. database
Session ........................................................... database
Storage ....................................................................
public/storage .................................................. NOT LINKED
$ grep -E '^(DB_CONNECTION|SESSION_DRIVER|CACHE_STORE|QUEUE_CONNECTION|MAIL_MAILER)=' .env
DB_CONNECTION=sqlite
SESSION_DRIVER=database
QUEUE_CONNECTION=database
CACHE_STORE=database
MAIL_MAILER=log
$ composer check-platform-reqs
Checking platform requirements for packages in the vendor dir
composer-runtime-api 2.2.2 success
ext-ctype * success provided by symfony/polyfill-ctype
ext-dom 20031129 success
ext-fileinfo 8.5.11 success
ext-filter 8.5.11 success
ext-hash 8.5.11 success
ext-iconv 8.5.11 success
ext-json 8.5.11 success
ext-libxml 8.5.11 success
ext-mbstring * success provided by symfony/polyfill-mbstring
ext-openssl 8.5.11 success
ext-pcre 8.5.11 success
ext-phar 8.5.11 success
ext-session 8.5.11 success
ext-tokenizer 8.5.11 success
ext-xml 8.5.11 success
ext-xmlwriter 8.5.11 success
php 8.5.11 success
$ grep 'Your Composer dependencies' vendor/composer/platform_check.php
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.4.1". You are running ' . PHP_VERSION . '.';
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 about_session.py
about has four blocks. Environment says local with debug mode on, because the skeleton’s .env sets APP_ENV=local and APP_DEBUG=true; both change on a live server. Cache says all four caches are NOT CACHED, which is right while you develop. Drivers shows what Laravel will use for each service: SQLite for the database, and the database driver for sessions, cache and queue, each set by one line in .env. Mail goes to the log, so no message leaves your computer. Storage says public/storage is not linked yet.
The platform check prints one line for each requirement, and all 18 lines say success. A line that ends with provided by symfony/polyfill-mbstring means a package supplies a stand-in for the extension, so Composer is satisfied without it. Composer’s manual says this command ignores the config.platform setting and checks the real platform, which makes it the check to run on a server. Laravel 13.34 asks for seven PHP extension: A module that adds functions or classes to PHP, such as mbstring for text or pdo_sqlite for SQLite databases. Laravel needs several of them.: ctype, filter, hash, mbstring, openssl, session and tokenizer.
The last line of the session is a surprise worth reading. Laravel 13 allows PHP 8.3, yet the packages Composer locked on PHP 8.5.11 include Symfony 8.1 components that need PHP 8.4.1, and Composer wrote the highest minimum it found into vendor/composer/platform_check.php. That file runs when the autoloader loads and stops the application with a clear message on an older PHP. Composer chooses package versions that fit the PHP it runs on, so the same command on PHP 8.3 may lock older releases.
Checkpoint
Check: Checking an install
composer run dev starts four processes in one terminal
The installation guide says that after npm install && npm run build you start everything with composer run dev, and open http://localhost:8000. The command does less than it seems. It runs a script from composer.json, which runs php artisan dev, and that starts the processes. You can list them without starting any, which is how the recording below shows them, because the Vite process needs Node and npm packages that these recordings do not install:
"""Lists the processes composer run dev starts, without starting them."""
from session import run
run(r"""
grep -A 3 '"dev": \[' composer.json
composer run --list
php artisan dev:list
""", app=True) Output
$ grep -A 3 '"dev": \[' composer.json
"dev": [
"Composer\\Config::disableProcessTimeout",
"@php artisan dev"
],
$ composer run --list
scripts:
setup Runs the setup script as defined in composer.json
dev Runs the dev script as defined in composer.json
test Runs the test script as defined in composer.json
post-autoload-dump
post-update-cmd
post-root-package-install
post-create-project-cmd
pre-package-uninstall Runs the pre-package-uninstall script as defined in composer.json
$ php artisan dev:list
server php artisan serve Illuminate\Foundation\Providers\ArtisanServicePro…
queue php artisan queue:listen --tries=1 --timeout=0 Illuminate\Foundatio…
logs php artisan pail --timeout=0 Illuminate\Foundation\Providers\Artisa…
vite npm run dev Illuminate\Foundation\Providers\ArtisanServiceProvider@…
Showing [4] dev commands
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 dev_list_session.py
The first lines show the dev script: it switches off Composer’s process time limit and then calls @php artisan dev. The table lists four named processes: server (php artisan serve), queue (a worker that listens for jobs), logs (Pail, which prints log lines as they are written) and vite (npm run dev, which rebuilds CSS and JavaScript when you save). The table is cut at 80 columns, so the last column of each row shows where the process was registered. Walk through the command once:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- You type one command. composer run dev runs the dev script in composer.json. The script turns off Composer's time limit, so the long-running processes are not cut off, and then runs php artisan dev.
- Artisan takes over. php artisan dev starts every process needed for local work in one terminal window. It needs Node 22.13 or later, because it manages the processes with an npm package.
- The web server starts. The server process runs php artisan serve, and the page is then at http://localhost:8000. This process is all a learner needs to see the notices page of the next lessons.
- A queue worker listens. The queue process runs queue:listen with one try for each job and no time limit. The default queue stores jobs in the database, so this worker is waiting on the jobs table.
- Logs are tailed. The logs process runs Pail, which prints the application's log lines as they are written, so errors show up in the same window as the server.
- Vite watches the assets. The vite process runs npm run dev, which rebuilds CSS and JavaScript when you save. It needs Node and installed npm packages, so it cannot run in this lesson's recordings.
- A crash restarts a process. If a process crashes, Laravel restarts it after a short delay, up to five times. One that dies within a second of starting is not restarted, because it most likely never started.
The dev command needs Node 22.13 or later and, on Windows, falls back to the concurrently npm package without the tabbed screen. Vite itself needs Node 20.19 or 22.12 and later. To run only some processes, DevCommands::only('server', 'vite') in a service provider filters them, and php artisan dev --no-restart turns off the automatic restarts.
Two terminals do the same job
Without Node, php artisan serve alone serves the pages for the first lessons. Add the queue worker and the Vite process when a lesson needs them.
Checkpoint
Check: The dev processes
Three setup errors, each with a one-line fix
Most first-day failures are one of three messages. Reproduce the first two in a recorded session: running artisan in the wrong folder, and starting from a project that has no application key, as every clone from Git does.
"""Two setup errors that artisan reports itself: run outside the project, and a missing application key."""
from session import run
run("""
# Error 1: artisan, one folder above the project
cd ..
!php artisan about
cd pathshala
php artisan --version
# Error 2: a project copied from Git has no .env, only .env.example
cp .env.example .env
grep -c '^APP_KEY=$' .env
!php artisan tinker --execute="echo encrypt('hello');"
php artisan key:generate
grep -c '^APP_KEY=base64:' .env
php artisan tinker --execute="echo decrypt(encrypt('hello'));"
""", app=True) Output
# Error 1: artisan, one folder above the project
$ cd ..
$ php artisan about
Could not open input file: artisan
[exit status 1]
$ cd pathshala
$ php artisan --version
Laravel Framework 13.34.0
# Error 2: a project copied from Git has no .env, only .env.example
$ cp .env.example .env
$ grep -c '^APP_KEY=$' .env
1
$ php artisan tinker --execute="echo encrypt('hello');"
Illuminate\Encryption\MissingAppKeyException No application encryption key has been specified.
[exit status 1]
$ php artisan key:generate
INFO Application key set successfully.
$ grep -c '^APP_KEY=base64:' .env
1
$ php artisan tinker --execute="echo decrypt(encrypt('hello'));"
hello
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 errors_session.py
Could not open input file: artisan means PHP looked for a file called artisan in the current folder and found none. artisan is a file at the project root, so the fix is cd into the folder that holds artisan and composer.json. The second error appears because the installation guide says .env should not be committed to source control, so a clone has only .env.example, whose APP_KEY= is empty. Laravel’s encryption guide says the encrypter needs that key and tells you to create it with php artisan key:generate. After the fix, encrypt and decrypt round-trip the word hello.
Keep the key out of Git, tickets and chat
The key protects everything Laravel encrypts. Each environment gets its own, made by key:generate, and a real one is never pasted into a repository, a ticket or a message.
The third error shows up in a browser. A page that asks Vite for its files before they are built answers with a 500. This project has no public/build/manifest.json, because npm run build has not run. The session serves two pages from one small overlay, one that asks for the files and one that checks first, the way the skeleton’s own welcome page does:
routes/web.php · vite-page/routes/web.php
<?php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
// A page that asks Vite for its CSS and JavaScript with no guard.
Route::get('/vite-demo', function () {
return view('vite-demo');
});
// The same page with the guard that the welcome view uses.
Route::get('/vite-guarded', function () {
return view('vite-guarded');
}); vite-page/resources/views/vite-demo.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Pathshala</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
<h1>Pathshala notices</h1>
</body>
</html> vite-page/resources/views/vite-guarded.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Pathshala</title>
@if (file_exists(public_path('build/manifest.json')) || file_exists(public_path('hot')))
@vite(['resources/css/app.css', 'resources/js/app.js'])
@endif
</head>
<body>
<h1>Pathshala notices</h1>
</body>
</html> """A page that asks for Vite's files before they exist: the 500, its cause and two ways out."""
from session import serve
serve(port=8213, requests=r"""
curl -s -o /dev/null -w "status %{http_code}\n" http://127.0.0.1:8213/vite-demo
curl -s -H "Accept: application/json" http://127.0.0.1:8213/vite-demo | grep '"message"'
curl -s -o /dev/null -w "status %{http_code}\n" http://127.0.0.1:8213/vite-guarded
""", overlay=("vite-page",)) Output
# Terminal 1: start the server
$ php artisan serve --host=127.0.0.1 --port=8213 --no-reload
# Terminal 2: send requests
$ curl -s -o /dev/null -w "status %{http_code}\n" http://127.0.0.1:8213/vite-demo
status 500
$ curl -s -H "Accept: application/json" http://127.0.0.1:8213/vite-demo | grep '"message"'
"message": "Vite manifest not found at: public/build/manifest.json (View: resources/views/vite-demo.blade.php)",
$ curl -s -o /dev/null -w "status %{http_code}\n" http://127.0.0.1:8213/vite-guarded
status 200
# Terminal 1 showed this, then Ctrl+C stopped the server:
INFO Server running on [http://127.0.0.1:8213].
Press Ctrl+C to stop the server
… /vite-demo ........................................ ~ …
… /vite-demo ........................................ ~ …
… /vite-guarded ..................................... ~ …
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 vite_session.py
The unguarded page answers status 500 (the HTTP status codes tool lists what each code means), and asking for JSON shows the message: Vite manifest not found, with the path it looked in. The guarded page answers status 200. The real fix is to build the assets with npm install && npm run build, or keep npm run dev running, which the vite process of composer run dev does. The guard is a stop-gap for a page that must work before any build. Other first-day failures have the same shape, a message that names the missing piece: a PHP older than 8.3, a missing extension, and a Node too old for Vite.
Checkpoint
Check: Three setup errors
A setup report names the check that failed
Messages arrive one at a time, in the order you trip over them. A report lists every check at once. doctor.php is a small PHP script of this lesson: it checks the PHP version, eight extensions and five things about the project, and prints the result as JSON. It reads .env only to see whether APP_KEY starts with base64:, and it never prints the key. The script follows, then a recorded session that runs it twice:
<?php
// A setup check for a Laravel 13 project. Run `php doctor.php` inside the project,
// or `php doctor.php path/to/project`. It prints a JSON report and changes nothing.
$folder = rtrim($argv[1] ?? '.', '/\\');
// What laravel/framework 13.34 requires, plus the driver the default .env uses (SQLite).
$extensions = ['ctype', 'filter', 'hash', 'mbstring', 'openssl', 'session', 'tokenizer', 'pdo_sqlite'];
// Read .env as plain text: the key is only checked, never printed.
$values = [];
if (is_file($folder.'/.env')) {
foreach (file($folder.'/.env', FILE_IGNORE_NEW_LINES) as $line) {
if (preg_match('/^([A-Z_]+)=(.*)$/', $line, $match)) {
$values[$match[1]] = $match[2];
}
}
}
$checks = ['php-version' => PHP_VERSION_ID >= 80300 ? 'ok' : 'fail'];
foreach ($extensions as $name) {
$checks['ext-'.$name] = extension_loaded($name) ? 'ok' : 'fail';
}
$checks['artisan'] = is_file($folder.'/artisan') ? 'ok' : 'fail';
$checks['vendor'] = is_file($folder.'/vendor/autoload.php') ? 'ok' : 'fail';
$checks['env-file'] = is_file($folder.'/.env') ? 'ok' : 'fail';
$checks['app-key'] = str_starts_with($values['APP_KEY'] ?? '', 'base64:') ? 'ok' : 'fail';
$usesSqlite = ($values['DB_CONNECTION'] ?? 'sqlite') === 'sqlite';
$checks['sqlite-file'] = ! $usesSqlite || is_file($folder.'/database/database.sqlite') ? 'ok' : 'fail';
$failed = count(array_filter($checks, fn (string $result): bool => $result !== 'ok'));
echo json_encode(
['php' => PHP_VERSION, 'failed' => $failed, 'checks' => $checks],
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
), "\n";
exit($failed === 0 ? 0 : 1); """Runs the setup check inside the project, then on a folder that is not a project."""
from session import run
run("""
php doctor.php
cd ..
!php pathshala/doctor.php .
""", app=True, overlay=("doctor-tool",)) Output
$ php doctor.php
{
"php": "8.5.11",
"failed": 0,
"checks": {
"php-version": "ok",
"ext-ctype": "ok",
"ext-filter": "ok",
"ext-hash": "ok",
"ext-mbstring": "ok",
"ext-openssl": "ok",
"ext-session": "ok",
"ext-tokenizer": "ok",
"ext-pdo_sqlite": "ok",
"artisan": "ok",
"vendor": "ok",
"env-file": "ok",
"app-key": "ok",
"sqlite-file": "ok"
}
}
$ cd ..
$ php pathshala/doctor.php .
{
"php": "8.5.11",
"failed": 5,
"checks": {
"php-version": "ok",
"ext-ctype": "ok",
"ext-filter": "ok",
"ext-hash": "ok",
"ext-mbstring": "ok",
"ext-openssl": "ok",
"ext-session": "ok",
"ext-tokenizer": "ok",
"ext-pdo_sqlite": "ok",
"artisan": "fail",
"vendor": "fail",
"env-file": "fail",
"app-key": "fail",
"sqlite-file": "fail"
}
}
[exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 doctor_session.py
Inside the project every check says ok and the exit status is 0. Pointed at the parent folder, five checks fail, failed is 5 and the exit status is 1, so a shell script or a CI step can stop on it. Each failing name points at one fix: artisan means the wrong folder, vendor means composer install has not run, env-file and app-key mean the steps of the second error above. The second program reads three reports of the same shape and explains each failed check. Copy it, change one ok to fail in a report, and run php explain_report.php to see the advice for that check:
<?php
// Reads setup reports in the shape doctor.php prints and explains every check that failed.
$advice = [
'php-version' => ['Laravel 13 needs PHP 8.3 or newer.', 'install PHP 8.3, 8.4 or 8.5, then open a new terminal'],
'ext-mbstring' => ['PHP has no mbstring extension, which the framework needs for text.', 'enable it in php.ini or install the extension package'],
'ext-pdo_sqlite' => ['The default database is SQLite and PHP cannot open it.', 'enable pdo_sqlite, or pick another database in .env'],
'artisan' => ['This folder is not a Laravel project: there is no artisan file.', 'cd into the project folder'],
'vendor' => ['The Composer packages are not installed.', 'composer install'],
'env-file' => ['There is no .env file, so no settings.', 'copy .env.example to .env'],
'app-key' => ['APP_KEY is empty, and encryption needs it.', 'php artisan key:generate'],
'sqlite-file' => ['The SQLite database file does not exist.', 'create database/database.sqlite, then php artisan migrate'],
];
$reports = [
'A project just made by create-project' => '{"php":"8.5.11","failed":0,"checks":{"php-version":"ok","artisan":"ok","vendor":"ok","env-file":"ok","app-key":"ok","sqlite-file":"ok"}}',
'A clone from Git, before any setup' => '{"php":"8.5.11","failed":4,"checks":{"php-version":"ok","artisan":"ok","vendor":"fail","env-file":"fail","app-key":"fail","sqlite-file":"fail"}}',
'A laptop with an old PHP' => '{"php":"8.2.30","failed":2,"checks":{"php-version":"fail","ext-mbstring":"fail","artisan":"ok","vendor":"ok","env-file":"ok","app-key":"ok","sqlite-file":"ok"}}',
];
foreach ($reports as $title => $json) {
$report = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
echo $title, ' (PHP ', $report['php'], ', ', $report['failed'], " failed)\n";
foreach ($report['checks'] as $name => $result) {
if ($result === 'ok') {
continue;
}
[$why, $fix] = $advice[$name];
printf(" %-14s %s\n", $name, $why);
echo ' fix: ', $fix, "\n";
}
if ($report['failed'] === 0) {
echo " every check passed\n";
}
} Output
A project just made by create-project (PHP 8.5.11, 0 failed)
every check passed
A clone from Git, before any setup (PHP 8.5.11, 4 failed)
vendor The Composer packages are not installed.
fix: composer install
env-file There is no .env file, so no settings.
fix: copy .env.example to .env
app-key APP_KEY is empty, and encryption needs it.
fix: php artisan key:generate
sqlite-file The SQLite database file does not exist.
fix: create database/database.sqlite, then php artisan migrate
A laptop with an old PHP (PHP 8.2.30, 2 failed)
php-version Laravel 13 needs PHP 8.3 or newer.
fix: install PHP 8.3, 8.4 or 8.5, then open a new terminal
ext-mbstring PHP has no mbstring extension, which the framework needs for text.
fix: enable it in php.ini or install the extension package
Recorded with PHP 8.5.11 (cli) (NTS) on macOS 26 arm64. To run it yourself: php explain_report.php
Checkpoint
Check: A setup report
Practise: from an empty folder to a checked project
The questions below follow the lesson from the install routes to the report, starting with the idea you have shown least. Each answer says why it is right or what it misses.
Practice that adapts to you
Practise this lesson
14 questions about 7 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher): what does composer.lock record, and should an application commit it?
The file records the exact version of every package that Composer resolved from composer.json, including the packages those packages need. composer install reads it and installs exactly those versions, so a teammate, the build server and the production server all get the same code. composer update ignores it, resolves the packages again within the limits of composer.json and rewrites the file. Composer’s manual tells you to commit the lock file for an application, and the create-project run in this lesson wrote one for 109 packages. A library that other projects depend on is the exception, because the projects that use it resolve their own versions.
Warm-up (fresher): how does composer create-project laravel/laravel differ from laravel new?
Both start from the same skeleton package, laravel/laravel. composer create-project downloads it and runs the scripts in its composer.json: copy .env, install the packages, generate the key, create the SQLite file and migrate. It asks nothing and needs only Composer. laravel new is the Laravel installer, a separate program. It asks about the project name, a starter kit, the testing framework and the database first, then builds a project that matches the answers. Use create-project for the plain defaults or a script that cannot answer questions, and laravel new when you want a starter kit or Pest.
Two longer questions from this lesson start where the warm-ups stop:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Mid to senior
Two branches each added a Composer package to a Laravel app. Merging them conflicts in composer.lock, and a teammate keeps the file from one branch and commits. The build server's composer install then stops with a message that a required package is not in the lock file. What happened, and how do you resolve such a conflict?
-
Mid to senior
composer.json requires php ^8.3. Developers run PHP 8.5 and production runs 8.3. A developer uses array_find, added in PHP 8.4. Composer installs cleanly on the server, yet a page there fails with a call to an undefined function. Why did nothing warn you, and how do you catch this before deploying?
Flashcards · 7 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
Which routes lead to PHP, Composer and Laravel 13 on a new machine?
php.new, Laravel Herd, your own PHP with Composer, or Sail in Docker. Laravel 13 needs PHP 8.3 or newer; the track itself runs exact versions through mise so its recordings can be repeated.
-
What does composer create-project laravel/laravel do in one command?
It clones the skeleton, installs the packages from the lock file and runs three setup scripts. The result has 109 packages, a .env file, an application key and a SQLite database with nine tables.
-
How does laravel new differ from composer create-project?
laravel new asks about a starter kit, the testing framework and the database before it builds the project. create-project asks nothing and keeps the plain defaults.
-
Which two commands show in seconds whether a new install is sound?
php artisan about summarises the project: its versions, drivers and settings. composer check-platform-reqs compares the PHP on this computer, with its extensions, against what the installed packages ask for, so a too-old PHP or a missing extension shows at once.
-
What starts when you run composer run dev in a new Laravel 13 project?
It calls php artisan dev, which starts four processes in one terminal: the development server, a queue worker, Pail for the log and Vite for the front-end assets.
-
What do the three most common first-day setup errors mean?
Could not open input file: artisan means the command ran outside the project folder; an empty APP_KEY in a fresh clone needs php artisan key:generate; Vite manifest not found means the assets were never built, so run npm run build (or npm run dev).
-
What does composer.lock record, and should an application commit it?
The exact version of every package Composer resolved, dependencies of dependencies included. composer install reads it, so every computer gets the same code, while composer update resolves again and rewrites it. An application commits it.
Key takeaways
- Laravel 13 needs PHP 8.3 or newer, and php.new, Herd, your own PHP with Composer, or Sail each get you there; this track uses mise to run exact versions, so recordings can be repeated.
composer create-project laravel/laravelis a clone, an install from a lock file and three setup scripts, and it leaves 109 packages, a.env, an application key and a SQLite database with nine tables.laravel newasks about a starter kit, the testing framework and the database before it builds, whilecreate-projectasks nothing and keeps the plain defaults.php artisan aboutandcomposer check-platform-reqsshow the versions, drivers and extensions of a project in seconds, andcomposer run devstarts a server, a queue worker, Pail and Vite throughphp artisan dev.- The three first-day errors say what is missing:
artisanrun outside the project, an emptyAPP_KEYin a fresh clone, andVite manifest not foundbeforenpm run build.
Terms in this lesson
- application key
- The secret stored as APP_KEY in the environment file. Laravel's encrypter uses it, php artisan key:generate creates it, and each environment has its own.
- Composer
- PHP's dependency manager. It reads composer.json, downloads the packages a project needs from Packagist and installs them into the vendor folder.
- lock file
- composer.lock records the exact version of every package Composer resolved. composer install reads it, so every computer installs the same code.
- Packagist
- The default package repository for Composer. Composer asks it where to download a package such as laravel/framework.
- PHP extension
- A module that adds functions or classes to PHP, such as mbstring for text or pdo_sqlite for SQLite databases. Laravel needs several of them.
References
- Laravel 13.x documentation, Installation (Laravel (laravel.com))
- Laravel 13.x documentation, Release notes (Laravel (laravel.com))
- Laravel 13.x documentation, Starter kits (Laravel (laravel.com))
- Laravel 13.x documentation, Sail (Laravel (laravel.com))
- Laravel 13.x documentation, Configuration (Laravel (laravel.com))
- Laravel 13.x documentation, Artisan console (Laravel (laravel.com))
- Laravel 13.x documentation, Encryption (Laravel (laravel.com))
- Laravel 13.x documentation, Asset bundling (Vite) (Laravel (laravel.com))
- php.new, install PHP and Laravel in one command (Laravel (php.new))
- Laravel Herd documentation, Installation (Laravel (Herd))
- Laravel installer, NewCommand source (Laravel (GitHub))
- Composer documentation, Basic usage (Composer)
- Composer documentation, Command-line interface (Composer)
- About Packagist.org (Packagist)
- laravel/framework on Packagist (Packagist)
- mise documentation, Getting started (mise)
- symfony/console on Packagist (Packagist)
- Vite documentation, Getting started (Vite)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress