Skip to main content
Service providers registered at Laravel’s boot time are loaded on every request — even if that service is never used during a given request. Deferred Service Providers solve this problem by deferring the loading of a provider until its service is actually needed.
This page assumes familiarity with Laravel Package Development. Make sure you understand the fundamentals of service providers before reading on.

Why deferred providers matter

A typical service provider runs register() and boot() on every request. Initializing mail, queues, cache, and other features that are not needed on every page is wasteful.
// This provider's register() is called on every request
class ReportServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Binds a heavy reporting service every time
        $this->app->singleton(ReportGenerator::class, function ($app) {
            return new ReportGenerator(
                $app->make(PdfRenderer::class),
                $app->make(ChartRenderer::class),
                $app->make(DataExporter::class),
            );
        });
    }
}
By deferring this provider, initialization only happens on requests that actually generate a report.

The DeferrableProvider interface

Illuminate\Contracts\Support\DeferrableProvider is a simple interface with a single method: provides().
namespace Illuminate\Contracts\Support;

interface DeferrableProvider
{
    public function provides();
}
Implementing this interface is all it takes to put a provider into deferred mode.

Basic implementation

Implementing a deferred provider takes three steps.
1

Implement DeferrableProvider

use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class ReportServiceProvider extends ServiceProvider implements DeferrableProvider
{
    // ...
}
2

Register bindings in register()

Write your bindings in register() just like a regular provider.
public function register(): void
{
    $this->app->singleton(ReportGenerator::class, function ($app) {
        return new ReportGenerator(
            $app->make(PdfRenderer::class),
            $app->make(ChartRenderer::class),
            $app->make(DataExporter::class),
        );
    });

    $this->app->singleton(ReportRepository::class, function ($app) {
        return new ReportRepository($app->make('db'));
    });
}
3

Return registered services from provides()

provides() must return every service bound in register(). Laravel uses this list to determine which provider to load when a given service is requested.
public function provides(): array
{
    return [
        ReportGenerator::class,
        ReportRepository::class,
    ];
}

How the service manifest works

At boot time, Laravel generates a manifest file at bootstrap/cache/services.php. This file stores a mapping of all services provided by deferred providers.
// bootstrap/cache/services.php (auto-generated)
return [
    'providers' => [
        // same list as bootstrap/providers.php
    ],
    'eager' => [
        // eagerly loaded providers
        App\Providers\AppServiceProvider::class,
    ],
    'deferred' => [
        // service name => provider class mappings
        'App\Services\ReportGenerator' => ReportServiceProvider::class,
        'App\Repositories\ReportRepository' => ReportServiceProvider::class,
        'cache'       => Illuminate\Cache\CacheServiceProvider::class,
        'cache.store' => Illuminate\Cache\CacheServiceProvider::class,
        'queue'       => Illuminate\Queue\QueueServiceProvider::class,
    ],
    'when' => [],
];
Thanks to this manifest, Laravel knows which provider supplies which service without loading any provider files. The actual provider is only loaded the first time its service is resolved.
After adding or modifying providers, regenerate the manifest:
php artisan optimize:clear
# or
php artisan clear-compiled

Internal flow


The importance of provides()

If a binding is missing from provides(), that service will never be resolved.
public function register(): void
{
    $this->app->singleton(ReportGenerator::class, fn ($app) => new ReportGenerator());

    // Added binding
    $this->app->singleton('report', fn ($app) => $app->make(ReportGenerator::class));
}

public function provides(): array
{
    return [
        ReportGenerator::class,
        'report',  // ← string-keyed bindings must be listed too
    ];
}
The same applies when using the $bindings / $singletons properties — every key must be included in provides().
class AnalyticsServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public $singletons = [
        AnalyticsClient::class => DefaultAnalyticsClient::class,
    ];

    public function provides(): array
    {
        // Return every key listed in $singletons
        return [
            AnalyticsClient::class,
        ];
    }
}

Constraints of deferred providers

Deferred providers are designed solely for registering container bindings. Providers that do the following in boot() cannot be deferred.
ConstraintReason
Route registrationRoutes must be resolved at application boot time
Global middleware registrationMust be registered before the request pipeline runs
Event listeners (always needed)Must be registered before the event fires
Blade directive registrationMust be registered before views are compiled
You can write a boot() method in a deferred provider, but its contents will not run until the service is resolved. Placing “always needed” logic like route or middleware registration in boot() will lead to unexpected behavior.

The when() method — event-triggered loading

The when() method lets you load a provider when a specific event fires, rather than only when a service is resolved. This is useful for providers that are only needed in certain contexts, such as job processing.
class ReportServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->app->singleton(ReportGenerator::class, fn () => new ReportGenerator());
    }

    public function provides(): array
    {
        return [ReportGenerator::class];
    }

    /**
     * Load this provider when the given events fire,
     * even if the service itself has not been resolved yet.
     */
    public function when(): array
    {
        return [
            \App\Events\ReportRequested::class,
        ];
    }
}
When any event listed in when() fires, the provider is loaded even if none of its services have been directly resolved.

Using deferred providers in packages

When distributing a third-party package, a deferred service provider directly benefits the performance of the user’s application.
namespace Acme\Analytics;

use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class AnalyticsServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->mergeConfigFrom(__DIR__.'/../config/analytics.php', 'analytics');

        $this->app->singleton(AnalyticsManager::class, function ($app) {
            return new AnalyticsManager($app->make('config')->get('analytics'));
        });

        $this->app->singleton('analytics', fn ($app) => $app->make(AnalyticsManager::class));
    }

    public function boot(): void
    {
        // Keep boot() limited to publish registration
        if ($this->app->runningInConsole()) {
            $this->publishes([
                __DIR__.'/../config/analytics.php' => config_path('analytics.php'),
            ], 'analytics-config');
        }
    }

    public function provides(): array
    {
        return [
            AnalyticsManager::class,
            'analytics',
        ];
    }
}
mergeConfigFrom() internally checks for a cached config, so it is safe to call inside a deferred provider’s register(). However, if config is already cached, it has no effect.

Separating Artisan command registration with runningInConsole()

Command registration is only needed when Artisan is running, so guard it with runningInConsole(). If you want to defer a provider that also registers commands, either include the command classes in provides() or create a separate provider for commands.
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->commands([
            AnalyticsFlushCommand::class,
        ]);
    }
}

Deferred providers in Laravel core

Many of Laravel’s built-in providers are deferred, avoiding eager initialization of services that are not used on every request.
ProviderServices provided
CacheServiceProvidercache, cache.store, RateLimiter
QueueServiceProviderqueue, queue.worker, queue.failer
MailServiceProvidermail.manager, mailer
RedisServiceProviderredis, redis.connection
HashServiceProviderhash, hash.driver
ValidationServiceProvidervalidator, validation.presence
TranslationServiceProvidertranslator
BroadcastServiceProviderBroadcast
In an API-only application, providers like MailServiceProvider and BroadcastServiceProvider may never be loaded at all during a request.

When to defer (and when not to)

  • Services not used on every request (mailers, report generators, external API clients, etc.)
  • Services requiring external connections or file I/O to initialize
  • Services with a heavy object graph
  • Providers that only register CLI commands
  • Providers that register routes (e.g., loadRoutesFrom)
  • Providers that register always-on middleware or exception handlers
  • Providers that register Eloquent global scopes or observers
  • Lightweight services used on the majority of requests (deferred overhead may exceed the benefit)

Laravel Package Development

A comprehensive guide to developing Laravel packages centered on service providers.

Package Versioning and Compatibility

Strategies for maintaining packages across major Laravel and PHP version upgrades.
Last modified on June 6, 2026