Saltar al contenido principal
Los service providers registrados durante el arranque de Laravel se cargan siempre, incluso si sus funcionalidades no se usan ni una vez durante la petición. Los service providers diferidos (Deferred Service Provider) resuelven este problema: retrasan la carga del provider hasta que sus servicios se usan realmente.
Esta página da por sabido el contenido de Fundamentos del desarrollo de paquetes. Es recomendable entender primero cómo funciona un service provider normal.

Por qué necesitas providers diferidos

Un service provider normal ejecuta register() y boot() en cada petición. Es un desperdicio inicializar en todas las páginas funcionalidades como el envío de correos, colas o caché si no se usan en muchas de ellas.
// register() de este provider se llama en todas las peticiones
class ReportServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Enlazamos un servicio de reports pesado en cada request
        $this->app->singleton(ReportGenerator::class, function ($app) {
            return new ReportGenerator(
                $app->make(PdfRenderer::class),
                $app->make(ChartRenderer::class),
                $app->make(DataExporter::class),
            );
        });
    }
}
Si haces este provider diferido, solo se inicializará en las peticiones que generen realmente un report.

Interfaz DeferrableProvider

Illuminate\Contracts\Support\DeferrableProvider es una interfaz muy simple con un único método provides().
namespace Illuminate\Contracts\Support;

interface DeferrableProvider
{
    public function provides();
}
Basta con implementar esta interfaz para que el provider pase a modo diferido.

Implementación básica

La implementación de un provider diferido consta de tres pasos.
1

Implementar DeferrableProvider

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

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

Enlazar los servicios en register()

Igual que en un provider normal, define los bindings en register().
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

Devolver los servicios registrados en provides()

provides() debe devolver todos los servicios que has enlazado en register(). Laravel usa esta lista para saber al pedir qué servicio debe cargar este provider.
public function provides(): array
{
    return [
        ReportGenerator::class,
        ReportRepository::class,
    ];
}

Cómo funciona el manifest de servicios

En el arranque, Laravel genera un archivo llamado bootstrap/cache/services.php. Este manifest contiene la lista de servicios que ofrecen los providers diferidos.
// Ejemplo de bootstrap/cache/services.php (generado automáticamente)
return [
    'providers' => [
        // La misma lista de bootstrap/providers.php
    ],
    'eager' => [
        // Providers de carga inmediata
        App\Providers\AppServiceProvider::class,
    ],
    'deferred' => [
        // Mapping servicio => clase del provider
        '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' => [],
];
Gracias a este manifest, Laravel sabe qué provider ofrece cada servicio sin necesidad de cargar el archivo del provider. El provider real solo se carga cuando el servicio se resuelve por primera vez.
Cuando añadas o modifiques providers, regenera el manifest.
php artisan optimize:clear
# o bien
php artisan clear-compiled

Flujo interno de funcionamiento


Importancia del método provides()

Si en provides() te olvidas de algún servicio, ese servicio no se resolverá nunca.
public function register(): void
{
    $this->app->singleton(ReportGenerator::class, fn ($app) => new ReportGenerator());

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

public function provides(): array
{
    return [
        ReportGenerator::class,
        'report',  // ← no olvides incluir también las claves por string
    ];
}
Lo mismo aplica si usas las propiedades $bindings / $singletons.
class AnalyticsServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public $singletons = [
        AnalyticsClient::class => DefaultAnalyticsClient::class,
    ];

    public function provides(): array
    {
        // Devuelve todas las claves declaradas en $singletons
        return [
            AnalyticsClient::class,
        ];
    }
}

Limitaciones de los providers diferidos

Los providers diferidos son adecuados solo para providers cuyo único cometido es registrar bindings en el contenedor. Los providers que hacen lo siguiente en boot() no pueden ser diferidos.
LimitaciónMotivo
Registro de rutasLas rutas se resuelven al arrancar la aplicación, así que en modo diferido no funcionaría.
Registro de middlewares globalesDebe estar registrado antes de procesar la petición.
Registro de listeners de eventos (los que se necesitan siempre)Si el listener no está registrado antes de que se dispare el evento, no se ejecuta.
Añadir directivas de BladeDebe estar registrado antes de compilar las vistas.
Puedes escribir un método boot() en un provider diferido, pero su contenido no se ejecutará hasta que se resuelva el servicio. Si en boot() haces cosas «que siempre se necesitan», como rutas o middlewares, obtendrás un comportamiento inesperado.

Método when() — registro por evento

Con el método when() puedes hacer que un provider se registre cuando se dispare un evento concreto. Es útil para providers que solo se necesitan en contextos específicos, como procesos de jobs.
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];
    }

    /**
     * Además de al resolver los bindings,
     * carga el provider cuando se disparen los eventos indicados.
     */
    public function when(): array
    {
        return [
            \App\Events\ReportRequested::class,
        ];
    }
}
Cuando se dispare uno de los eventos devueltos por when(), el provider se cargará aunque no se haya resuelto directamente ningún servicio.

Uso en el desarrollo de paquetes

Al distribuir un paquete de terceros, los providers diferidos también contribuyen al rendimiento de las aplicaciones de tus usuarios.

Patrón recomendado

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
    {
        // Limita boot() al registro de publicaciones
        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() comprueba internamente si la configuración ya está cacheada, así que es seguro llamarlo dentro del register() de un provider diferido. Eso sí, si la configuración está cacheada, no hará nada.

Separar el registro de comandos Artisan con runningInConsole()

Como el registro de comandos solo es necesario al arrancar Artisan, protégelo con runningInConsole(). Si además quieres diferir el provider que registra los comandos, incluye las clases de comando en provides() o proporciona un provider separado dedicado a los comandos.
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->commands([
            AnalyticsFlushCommand::class,
        ]);
    }
}

Ejemplos en el core de Laravel

Muchos de los providers del core de Laravel son diferidos. Es un diseño pensado para no cargar de forma inmediata todos los servicios que no se usan en cada petición.
ProviderServicios que provee
CacheServiceProvidercache, cache.store, RateLimiter
QueueServiceProviderqueue, queue.worker, queue.failer
MailServiceProvidermail.manager, mailer
RedisServiceProviderredis, redis.connection
HashServiceProviderhash, hash.driver
ValidationServiceProvidervalidator, validation.presence
TranslationServiceProvidertranslator
BroadcastServiceProviderBroadcast
En una aplicación con solo endpoints de API puede darse el caso de que MailServiceProvider o BroadcastServiceProvider no se carguen nunca durante una petición.

Criterios para decidir si diferir

  • Servicios que no se usan en todas las peticiones (correo, reports, clientes de APIs externas, etc.)
  • Servicios cuya inicialización requiere conexiones externas o lectura de archivos.
  • Servicios con grafos de objetos pesados.
  • Providers que solo aportan comandos usados por CLI.
  • Providers que registran rutas (loadRoutesFrom, etc.).
  • Providers que registran middlewares o handlers de excepciones activos permanentemente.
  • Providers que registran global scopes u observers de Eloquent.
  • Servicios ligeros usados en la mayoría de peticiones (donde la sobrecarga del diferido puede ser mayor).

Páginas relacionadas

Fundamentos del desarrollo de paquetes

Aprende cómo desarrollar paquetes Laravel centrados en los service providers.

Gestión de la compatibilidad de versiones de paquetes

Estrategias de mantenimiento de paquetes ante grandes actualizaciones de versión de Laravel y PHP.
Última modificación el 13 de julio de 2026