A service provider is a class that runs while Laravel starts up, so you can register bindings in the container and configure services before any request is handled.
๐ชPicture a stage crew before a concert. First, every crew member unloads and lists their gear (register). Only once everything is on stage do they plug it all together and run the sound check (boot). Then the doors open.
Where your app gets set up
Home for bindings
The container bindings you learned about need a place to live. That place is a provider.
Laravel is built on them
Cache, database, mail, queues and sessions are each wired up by a framework provider. Packages ship their own, too.
App-wide settings
Gates, model rules, rate limiters and view composers all get configured in a provider's boot() method.
Two passes: register everything, then boot everything
bootstrap/providers.phpregister() calls before it starts any boot() call. That ordering is the whole reason two methods exist.register() | boot() | |
|---|---|---|
| Runs | First, on all providers | After every provider has registered |
| Purpose | Tell the container how to build things | Use services to configure the app |
| Safe to use other services? | โ No, they may not be registered yet | โ Yes |
| Typical code | bind(), singleton(), scoped() | Gate::define(), RateLimiter::for(), View::composer(), Model::preventLazyLoading() |
Working with providers
-
Meet
AppServiceProviderA fresh Laravel 11+ app ships with exactly one provider of your own:
app/Providers/AppServiceProvider.php. For small and medium apps, it's often the only one you need.<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { public function register(): void { // } public function boot(): void { // } } -
Put bindings in
register()Inside a provider,
$this->appis the container.register()should only describe how to build things. Don't resolve other services here, because their providers may not have run yet.public function register(): void { $this->app->bind(Notifier::class, MailNotifier::class); $this->app->singleton(StripeClient::class, fn () => new StripeClient(config('services.stripe.secret')) ); }For simple class-to-class bindings, providers also offer shortcut properties:
public $bindings = [ Notifier::class => MailNotifier::class, ]; public $singletons = [ PaymentGateway::class => StripeGateway::class, ]; -
Configure the app in
boot()By the time
boot()runs, every service is registered, so you can use facades, models and other services freely. You can even type-hint dependencies inboot()'s signature and the container injects them.use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\RateLimiter; public function boot(): void { // Catch N+1 queries while developing Model::preventLazyLoading(! app()->isProduction()); Gate::define('publish-post', fn (User $user) => $user->is_editor); RateLimiter::for('comments', fn (Request $request) => Limit::perMinute(5)->by($request->user()?->id ?: $request->ip()) ); }You'll recognise these from Gates, Rate Limiting and Eager Loading.
-
Where providers are listed:
bootstrap/providers.phpIn Laravel 11 and later, your app's providers are listed in a tiny file. (Older versions kept a long
providersarray inconfig/app.php; you'll still see that in older tutorials.)<?php return [ App\Providers\AppServiceProvider::class, ];The framework's own providers (cache, database, mailโฆ) are loaded for you, and package providers are found automatically through package discovery: each package declares its provider in its
composer.json. -
Create your own with
make:providerWhen
AppServiceProvidergets crowded, split related setup into its own provider, such as one for billing:php artisan make:provider BillingServiceProviderThis creates
app/Providers/BillingServiceProvider.phpand adds it tobootstrap/providers.phpautomatically. If you create a provider file by hand, remember to add it to that list yourself.class BillingServiceProvider extends ServiceProvider { public function register(): void { $this->app->singleton(PaymentGateway::class, StripeGateway::class); } public function boot(): void { Gate::define('view-invoices', fn (User $user) => $user->is_admin); } }โน๏ธ Deferred providersA provider that only registers bindings can implement
Illuminate\Contracts\Support\DeferrableProviderand list its services in aprovides()method. Laravel then loads it only when one of those services is actually requested.
Avoid this, do that
public function register(): void
{
Gate::define('publish-post', ...);
View::share('categories', Category::all());
}
Using services in register() can fail because the services you're calling may not be registered yet.
public function boot(): void
{
Gate::define('publish-post', ...);
View::composer('layouts.app', CategoryComposer::class);
}
Bind in register(), use and configure in boot().
public function boot(): void
{
$this->settings = Setting::all(); // runs on EVERY request
}
boot() runs on every request and every Artisan command, even migrate on an empty database.
$this->app->singleton(Settings::class, fn () =>
new Settings(Setting::pluck('value', 'key'))
);
Make heavy work lazy: wrap it in a binding so it runs only when something actually needs it.
A provider should wire things together, not contain business logic. If a closure in a provider grows past a few lines, move that logic into a dedicated class (see Service Classes).
- Open
bootstrap/providers.phpin a fresh project and check which providers are listed. - Run
php artisan make:provider BlogServiceProviderand confirm it was added to that file. - In its
register(), bind aNotifierinterface to aLogNotifierclass. - In its
boot(), callModel::preventLazyLoading(! app()->isProduction()), then trigger an N+1 query and watch the exception appear.
Key takeaways
- Service providers are the startup scripts that configure Laravel and your app.
register()runs first on all providers; only bind things there.boot()runs after everything is registered; use services and configure the app there.- Laravel 11+ lists your providers in
bootstrap/providers.php. php artisan make:providercreates a provider and registers it for you.