Laravel Tutorials

Laravel Terminal UI for the artisan dev Command

Published
Laravel Terminal UI for the artisan dev Command image

Running php artisan dev starts four processes in one terminal UI: the development server, a queue worker, Pail, and Vite. Until Laravel 13.25 they all wrote to the same stream, which is fine until Vite rebuilds while a queue worker is logging and you are trying to read a stack trace from Pail.

The artisan dev command shelled out to concurrently, and Laravel 13.25 replaces it with @laravel/multiplex, a terminal UI that keeps each process separate. Nothing to install and nothing to configure, php artisan dev picks it up:

php artisan dev

The Three Modes

Tabs is the default. Each registered process gets its own tab, and you switch between them to read one process at a time. Individual processes can be restarted or have their logs cleared without touching the others, and a process that crashes restarts on its own.

Stream is the old behavior with better tooling: one interleaved feed, but scrollable and searchable rather than whatever your terminal's scrollback happens to hold.

Inline is plain output with no UI at all. It is selected automatically when there is no TTY, which is what you want when artisan dev runs under a process supervisor, in a container, or in CI.

Pick one for a single run:

php artisan dev --tabs
php artisan dev --stream
php artisan dev --inline

The short forms are -t, -s, and -i. You can also toggle between tabs and stream while the UI is running, so a flag is only needed when you want to start somewhere other than the default.

Search works in both tabs and stream mode, and timestamps can be turned on in any mode:

php artisan dev --timestamps

When the command exits, everything that was buffered is printed to the main terminal stream-style. Quitting the UI does not throw away the output you were looking at.

Setting the Default for a Project

The per-run flags are for one-offs. If everyone on the project should get stream mode, or if you want timestamps every time, set it in the boot method of a service provider next to your process registrations:

namespace App\Providers;
 
use Illuminate\Foundation\DevCommands;
use Illuminate\Support\ServiceProvider;
 
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
if (! $this->app->environment('local')) {
return;
}
 
DevCommands::stream();
DevCommands::withTimestamps();
}
}

Seven methods configure the command:

Method What it does
DevCommands::tabs() One tab per process (the default)
DevCommands::stream() One interleaved, scrollable feed
DevCommands::inline() Plain output, no UI
DevCommands::withTimestamps() Prefix every line with a timestamp
DevCommands::disableAutoRestart() Leave a crashed process dead instead of restarting it
DevCommands::bufferSize(5000) Lines kept per process in tabs mode
DevCommands::streamBufferSize(10000) Lines kept in total in stream mode

Command-line flags win over the provider, so php artisan dev --tabs overrides a DevCommands::stream() call and --no-restart overrides the auto-restart default for that run.

The buffer sizes are the two worth thinking about. Each process keeps its own ring buffer in tabs mode, so a chatty Vite process cannot push a queue worker's output out of view the way it does in a shared scrollback. Raise bufferSize() if you find yourself scrolling past the top of a tab.

Auto Restart

A crashed process restarts automatically. This is a real change in behavior from the --kill-others-on-fail that artisan dev passed to concurrently since 13.18, where one process dying took the whole stack down with it.

The new default suits the common case, a queue worker that dies on a fatal error while you are editing a job class, or Vite falling over on a bad import. You fix the file and the process comes back without you restarting the whole stack. When you would rather see the failure and stop, turn it off:

php artisan dev --no-restart

Or permanently, with DevCommands::disableAutoRestart().

What Runs Where

The multiplex path is macOS and Linux only for now. Windows keeps running through concurrently, and the mode and buffer methods are no-ops there, though --timestamps and --no-restart are both translated to the equivalent concurrently flags so those two work on every platform.

The other requirement is Node v22.13 or later, which is the floor for @laravel/multiplex. The framework pins the package to a specific minor version and runs it through your project's package manager exec command, so a pnpm project runs pnpm dlx and an npm project runs npx without any configuration. There is no dependency to add to package.json.

One more flag, mostly for tooling rather than for reading:

php artisan dev --json

That emits newline-delimited JSON events instead of formatted output, and implies --inline. It is the hook for wrapping artisan dev in something else, an editor extension or a dashboard that wants structured process output.

The SERVER_HOST Fix

A related change landed in the same release. The default server process was registered as serve --host=localhost, and because an explicit command-line option beats ServeCommand's environment-based default, SERVER_HOST was ignored once the application skeleton switched from the old composer dev script to artisan dev. Exposing the development server to another device on your network stopped working.

The default is now plain serve, so this behaves as it did before:

SERVER_HOST=0.0.0.0 php artisan dev

If you registered your own server process to work around this, you can delete it.

Further Reading

Paul Redmond photo

Staff writer at Laravel News. Full stack web developer and author.

Sponsored

acquaintsoft logo
Acquaint Softtech

Hire Laravel developers with AI expertise at $20/hr. Get started in 48 hours.

Visit Acquaint Softtech

The latest

View all →
Pause All Queues and a New artisan dev UI in Laravel 13.25 image

Pause All Queues and a New artisan dev UI in Laravel 13.25

Read article
Laravel monitoring that doesn't bill you by your traffic image

Laravel monitoring that doesn't bill you by your traffic

Read article
Mock PHP Classes in Tests With the Double Library image

Mock PHP Classes in Tests With the Double Library

Read article
Laravel Discount: Coupon Codes, Usage Limits, and Stacking image

Laravel Discount: Coupon Codes, Usage Limits, and Stacking

Read article
Laravel Truss: Live Interactive Database ER Diagrams image

Laravel Truss: Live Interactive Database ER Diagrams

Read article
Laravel artisan dev: Run Server, Queue, Logs, and Vite image

Laravel artisan dev: Run Server, Queue, Logs, and Vite

Read article