nunomaduro/laravel-starter-kit
A minimal, opinionated Laravel starter kit that provides a clean foundation with Pest, Laravel Pint, PHPStan, and GitHub Actions preconfigured. Skip boilerplate setup and start building your next Laravel app faster.
This is a skeleton, not a library — there is no composer require step. You scaffold a project from it and then live inside its opinionated toolchain.
laravel new my-app --using=nunomaduro/laravel-starter-kit
# or
composer create-project nunomaduro/laravel-starter-kit my-app
cd my-app
composer install && npm install
cp .env.example .env && php artisan key:generate && php artisan migrate
Look here first, in this order:
composer.json — the scripts block is the real entry point (composer run dev typically fans out to serve, queue:listen, pail, and Vite).phpstan.neon / larastan config — the strictness level sets your daily friction budget.rector.php, pint.json, phpunit.xml — the rest of the quality gate.AGENTS.md + .cursor/rules/laravel-boost.mdc — the behavioural contract for both you and any AI agent in the repo. Read these before writing code; they encode conventions the static analysis will enforce anyway..mcp.json — Laravel Boost wiring (^2.10.0 on current releases), if present.First use case: build one thin vertical slice to feel the loop end to end — a model with migration and factory, a form request, a controller, and a Pest feature test:
php artisan make:model Post -mf --no-interaction
php artisan make:test --pest PostTest --no-interaction
vendor/bin/pint --dirty --format agent
vendor/bin/phpstan analyse --memory-limit=1G
php artisan test --compact --filter=PostTest
Run phpstan and rector once before you write any code. On current releases both write their caches into a project-local tmp/ directory rather than the system /tmp — confirm tmp/ is gitignored the moment it appears, and commit that .gitignore entry early. If your first phpstan run exits instantly green, suspect a warm cache rather than a clean codebase.
If that four-command cycle feels natural, the kit is a fit. If PHPStan blocks you on the very first model, decide now whether you're willing to pay that tax — because it never gets cheaper, only more habitual.
Treat the quality gate as the unit of work. The productive rhythm in this kit is: write the failing Pest test → implement → pint --dirty → phpstan → php artisan test --compact --filter=.... Never hand off code that hasn't been through all four; the strictness is the product.
Type-safety idioms you'll repeat constantly:
declare(strict_types=1); at the top of every file, no exceptions.readonly for value objects and DTOs; avoid empty zero-arg constructors.FavoritePerson, not favorite_person) instead of string constants./** @param array{name: string, email: string} $data */./** @return Collection<int, Post> */.Creation is Artisan-first, always with flags. make:model -mf, make:test --pest, make:class, make:request — and always --no-interaction so the commands are scriptable and agent-safe. The guidelines forbid inventing base folders; extend the existing structure instead.
Use Boost's MCP tools as the outer loop rather than shell archaeology:
search-docs before touching framework code — it returns version-specific docs for the installed Laravel 13 / Pest 5 combination.database-schema before writing any migration or model.database-query for read-only investigation instead of raw SQL in Tinker.browser-logs when the frontend misbehaves, get-absolute-url before sharing any URL with a teammate.Encode your own conventions into the kit, not around it. Add architecture tests to tests/Arch.php for your domain (e.g. "no Illuminate\Support\Facades\DB outside App\Repositories"), and add custom Rector rules for refactors you keep doing by hand. The kit's value compounds when your rules live next to theirs.
Mirror the gate in CI so local and remote agree — the same pint --test, phpstan, rector --dry-run, and php artisan test --compact invocations, on a PHP 8.5 image, against the ^13.33.0 Laravel baseline. Two details worth copying deliberately when you lift the workflow: the kit pins recent action majors (actions/checkout@v7, actions/cache@v6, actions/upload-artifact@v7), so don't downgrade those to the versions you remember from other repos — and if you cache the analysis tools, point actions/cache at the project-local tmp/ path, since that is where Rector and PHPStan now write.
No upgrade path. Because this is a skeleton, adopting it means owning a fork. When upstream changes, you diff their repo against yours and cherry-pick. Budget for this, or pin to a commit and accept drift.
The version pins are aggressive. PHP 8.5 and Laravel 13 will not run on most shared hosts or stock CI images yet. Check config.platform in composer.json and confirm your deploy target before you invest a week of work. Current releases track Laravel ^13.33.0, so "Laravel 13" is a moving floor, not a fixed point — if you must downgrade, do it consciously rather than via a stream of --ignore-platform-reqs flags.
Caches now live in tmp/, not /tmp. This is a usability win (nothing to clean up system-wide, caches survive a reboot) but it changes three things: tmp/ must be gitignored, it must be excluded from Pint/Rector/PHPStan path arguments if you ever scan the whole project, and stale results after a config change are now your problem to clear — delete the directory or pass --clear-cache, don't assume the tool is lying to you. CI runners are ephemeral, so project-local caches only help you there if you explicitly cache tmp/.
CI action majors move fast. If you copied a workflow from an older starter kit, checkout@v4-era pins may already be unsupported by the time you read this. Diff your .github/workflows/ against upstream rather than assuming your fork is fine.
The two rule files disagree with each other. One says npm run dev, the other bun run dev; one lists PHPUnit v13, the other v12. Treat AGENTS.md as canonical for the checked-out commit and fix the drift in a PR — it's a real signal of how fresh the branch is.
Don't lower PHPStan to unblock yourself. The first week is painful; the fix is generics, typed array shapes, and proper DTOs, not level: 5. If you genuinely need an escape hatch, use a narrowly scoped @phpstan-ignore-next-line with a reason comment and grep for them in review. A creeping baseline file means you've quietly forked away from the kit's premise.
pint --dirty only formats changed files. It's correct for the inner loop but will let pre-existing style drift survive; run full vendor/bin/pint before opening a PR.
Pest 5 and PHPUnit 13 are new enough that third-party plugins lag. Check compatibility before adding snapshot, mutation, or parallel-testing extensions — a broken plugin here poisons the whole gate.
An empty or minimal test suite passes silently. php artisan test --compact exiting green on a suite with two tests is not a signal. Keep a smoke test that actually boots the app and hits a route.
Missing Boost tools make AGENTS.md noise. If search-docs or database-query aren't available in your editor, the instructions pointing at them become hallucination bait for AI agents. Verify the MCP server is registered (.mcp.json / php artisan boost:mcp) or strip those sections — and after a Boost bump (^2.10.0), re-verify the tool list rather than trusting the instructions.
Vite manifest errors mean assets weren't built: npm run build, or run composer run dev if you expected the dev server to be up.
Tinker etiquette: prefer tests over ad-hoc scripts (the guidelines say so explicitly), and when you do use it, single-quote the shell argument — php artisan tinker --execute 'Post::count();'.
Treat tests/Arch.php like production code. Deleting a failing arch test is the single fastest way to lose everything this kit gives you; a failure there is a design conversation, not an inconvenience.
How can I help you explore Laravel packages today?