Service Providers
Where every part of Laravel is bootstrapped — register bindings, then boot behaviour.
Service providers are the central place all bootstrapping happens. Every framework feature — the database, the queue, routing — is wired up by a provider. Your own providers are where you register bindings, macros, view composers, policies and event listeners.
register() vs boot()
This distinction trips up a lot of people, and getting it right is a senior signal.
register()— ONLY bind things into the container. Do not resolve services here; other providers may not have registered theirs yet.boot()— runs after all providers are registered. Safe to resolve and use anything. This is where you put macros, gates, event wiring.
public function register(): void
{
$this->app->singleton(Weather::class, OpenWeather::class);
}
public function boot(): void
{
// safe to touch other services here
URL::forceScheme('https');
}Deferred providers
If a provider only registers bindings (no boot work) you can implement DeferrableProvider so Laravel loads it lazily — only when one of its bindings is actually needed. A small but real boot-time win.
Example
<?php
namespace App\Providers;
use App\Contracts\Weather;
use App\Services\OpenWeatherClient;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\Facades\{Gate, URL};
use Illuminate\Support\ServiceProvider;
class WeatherServiceProvider extends ServiceProvider implements DeferrableProvider
{
/** register(): bindings ONLY — never resolve here. */
public function register(): void
{
$this->app->singleton(Weather::class, function ($app) {
return new OpenWeatherClient(config('services.weather.key'));
});
}
/** boot(): runs after all providers registered — safe to use services. */
public function boot(): void
{
if ($this->app->isProduction()) {
URL::forceScheme('https');
}
Gate::define('view-forecast', fn ($user) => $user->subscribed());
}
/** Because this provider is deferrable, list what it provides. */
public function provides(): array
{
return [Weather::class];
}
}When to use it
- A developer creates a BillingServiceProvider that registers all payment-related bindings and boots billing-related observers, grouping related setup in one place.
- A package author writes a service provider with a register() method so users can install the package and all its bindings are available after adding the provider to config/app.php.
- A deferred provider registers expensive bindings only when they are actually resolved, reducing bootstrap time for requests that never use those services.
More examples
Create and register a service provider
Artisan generates the provider class; registering it in config/app.php makes Laravel boot it on every request.
php artisan make:provider BillingServiceProvider
# Then add to config/app.php 'providers' array:
# App\Providers\BillingServiceProvider::classregister() and boot() in a provider
register() runs during binding phase (no other services available yet); boot() runs after all providers are registered so it can safely call other services.
class BillingServiceProvider extends ServiceProvider
{
public function register(): void
{
// Bind services — do NOT call other services here
$this->app->singleton(BillingService::class, function ($app) {
return new BillingService(
config('billing.stripe_key'),
$app->make(LoggerInterface::class)
);
});
}
public function boot(): void
{
// All providers are registered; safe to use services
Order::observe(OrderObserver::class);
View::share('currency', config('billing.currency'));
}
}Deferred service provider
Setting $defer = true prevents the provider from loading on every request; it bootstraps only when ReportGenerator is first resolved from the container.
class ReportingServiceProvider extends ServiceProvider
{
protected $defer = true;
public function register(): void
{
$this->app->singleton(ReportGenerator::class, function () {
return new ReportGenerator(config('reporting'));
});
}
public function provides(): array
{
return [ReportGenerator::class];
}
}
Discussion