In a Laravel 13 app, what does `composer run dev` start, and why use it instead of running `php artisan serve` alone?
answer
- a Composer script calling one Artisan command
- php artisan dev, added in 13.16
- server, queue, logs, vite
- queue:listen --tries=1 --timeout=0
- DevCommands::except() in a service provider
basics
~20 scomposer run dev calls php artisan dev, which by default runs the PHP development server, a queue listener, Pail log tailing and the Vite dev server together. artisan serve alone only answers HTTP, so queued jobs and Vite assets are left without a process.
solid answer
~40 sThe skeleton's `dev` script disables Composer's process timeout and runs `php artisan dev` (added in Laravel 13.16). That command starts four named processes: `server` (`php artisan serve`), `queue` (`php artisan queue:listen --tries=1 --timeout=0`), `logs` (`php artisan pail --timeout=0`, only when Pail is installed and `pcntl` is available) and `vite` (`npm run dev` through your package manager, when `package.json` exists). On macOS and Linux it runs them in the `@laravel/multiplex` tabbed interface and restarts a crashed process; on Windows it falls back to `concurrently`. `php artisan serve` alone gives you HTTP only: with the skeleton's `QUEUE_CONNECTION=database`, a queued reminder email just sits in the `jobs` table, and pages using `@vite` need either the dev server or a build. You can trim or extend the list with `DevCommands` in a service provider.
code
php · 18 lines<?php
namespace App\Providers;
use Illuminate\Foundation\DevCommands;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
// Herd already serves vet-clinic.test, so skip the PHP dev server
DevCommands::except('server');
// Also run the scheduler locally for appointment reminders
DevCommands::artisan('schedule:work', 'scheduler');
}
}go deeper
Know that composer run dev starts the web server, a queue listener, log tailing and Vite in one go, and that artisan serve alone only serves pages.
Explain the chain from the Composer script to php artisan dev, the four default processes and the conditions under which logs and vite are registered.
Shape the team's local loop with DevCommands (drop the server under Herd, add the scheduler) and keep dev-only settings such as one try and no timeout out of production workers.
Decide what the standard local loop contains across services, so every developer runs the same background processes the app depends on.
## The script and the command A freshly created Laravel 13 app has a `dev` entry in the `scripts` section of `composer.json`: - `Composer\Config::disableProcessTimeout` lifts Composer's default 300-second limit on scripts, so a long-running dev session is not killed. - `@php artisan dev` then runs the **`dev` Artisan command**, added in Laravel 13.16. So `composer run dev` is simply a convenient name; the work is done by `php artisan dev`, and you can run that directly. Earlier skeletons wired the same processes together inside the Composer script with the `concurrently` npm package. ## The four default processes `php artisan dev` asks the `Illuminate\Foundation\DevCommands` registry for its commands. The framework registers these defaults: | Name | Command | Registered when | |---|---|---| | `server` | `php artisan serve` | always | | `queue` | `php artisan queue:listen --tries=1 --timeout=0` | always | | `logs` | `php artisan pail --timeout=0` | Pail's provider is loaded and `pcntl_fork` exists | | `vite` | `npm run dev` (or pnpm, Yarn, Bun) | a `package.json` exists | On macOS and Linux the processes run under the **`@laravel/multiplex`** package, which gives each one a labelled, colour-coded tab, restarts a process that crashes (unless `--no-restart`), and prints the collected output when you quit; the docs say it needs Node 22.13 or later. On Windows the command falls back to `concurrently` without tabs. Flags such as `--stream`, `--tabs` and `--inline` change the display. ## Why `php artisan serve` alone is not enough `php artisan serve` starts PHP's built-in web server and nothing else. In a veterinary-clinic booking app that is enough to render pages, but: 1. **Queued work never runs.** The skeleton's default queue connection is the database, so an appointment-reminder job dispatched from a controller waits for a worker process. The `queue` process is that worker for local development. 2. **Front-end assets are missing.** Blade pages that use `@vite` need the Vite dev server running (or a production build in `public/build`). 3. **Logs stay in a file.** The `logs` process streams entries to the terminal as they are written. Starting all of them by hand means four terminals; `composer run dev` is one command that the whole team runs the same way. ## How the multiplexed session behaves A few details make the combined session easier to live with than four terminals: - Each process gets a **name** (`server`, `queue`, `logs`, `vite`) and a colour, so a stack trace in the queue tab is not confused with a Vite rebuild message. - If one process crashes, for example the queue listener hitting a fatal error, it is **restarted automatically**; pass `--no-restart` when you want it to stay down so you can read the error. - `--timestamps` prefixes every line with the time, and `--inline` prints plain output, which is also the default when the terminal is not a TTY, such as inside an editor's task runner. - Quitting ends the session and writes the collected output back to your terminal, so nothing printed during the session is lost. For the clinic app, a developer who books a test appointment sees the request in `server`, the reminder job in `queue`, the log line in `logs` and any front-end rebuild in `vite`, all in one window. ## Customizing the process list Register changes in the `boot()` method of `AppServiceProvider`: - `DevCommands::register('command', 'name')` adds any shell command; `DevCommands::artisan()` prefixes `php artisan` for you. - `DevCommands::except('server')` drops a default, useful when Herd or Valet already serves the site. - `DevCommands::only(...)` keeps only the named processes, and `DevCommands::order([...])` sets their order. - `php artisan dev:list` (13.17) shows what will run and where each command was registered. ## Common misreadings - It is not a production process manager; every process it starts is a development tool. - Its queue process runs each job with one try and no timeout, which suits local debugging, not a production worker. - `composer run dev` does not build assets for deployment; that is `npm run build`.
- How do you check which processes php artisan dev will start, and where each one was registered?Run `php artisan dev:list`, added in Laravel 13.17. It lists the registered dev processes after `only()`, `except()` and ordering are applied, and the registry records the source file and line of each registration, so a process added by a package's service provider can be traced. `DevCommands::withoutVendorCommands()` drops processes registered from `vendor/`.
- Which processes can be missing from php artisan dev, and why?`logs` is registered only when Pail's service provider is loaded and the `pcntl_fork` function exists, so Windows, which has no `pcntl`, gets no log tab. `vite` is registered only when `package.json` exists. `server` and `queue` are always registered unless you remove them with `DevCommands::except()` or `withoutDefaultCommands()`.
saying these in an interview costs you the question
- composer run dev builds production assets for deployment
- php artisan serve also runs the queue listener and Vite
- The dev queue process retries each failed job three times
- You change the dev processes by editing vendor files
- composer run dev and php artisan dev start different processes