What is a Service Provider?
A Service Provider is the central place where your application is wired up.
Everything Laravel boots — the mailer, queue, cache, auth system, routes, events — is set up
through a service provider.
A service provider is where you:
Bind interfaces to implementations (in register())
Register event listeners and model observers
Register middleware
Define routes and route macros
Set up view composers, Blade directives, validation rules (in boot())
Configure third-party packages
Think of service providers as your application's architecture manifest — reading all of them
tells you exactly what your app does and how it's wired.
The Two Phases – register() vs boot()
Every service provider has two phases. Understanding the difference between them is the single
most important thing to know about providers.
PHASE 1 — register()
─────────────────────────────────────────────────────────────────
Called first on ALL providers before any boot() runs.
Purpose: bind things into the service container — ONLY.
✅ DO: $this->app->bind(...)
✅ DO: $this->app->singleton(...)
❌ DON'T: use other services (they may not be registered yet)
❌ DON'T: register event listeners, routes, view composers here
PHASE 2 — boot()
─────────────────────────────────────────────────────────────────
Called after ALL providers have completed register().
Every service is available and safe to use here.
✅ DO: View::composer(...)
✅ DO: Event::listen(...)
✅ DO: Route::macro(...)
✅ DO: Validator::extend(...)
✅ DO: Model::observe(...)
✅ DO: Type-hint dependencies in boot() signature
Execution Order
Provider A → register() ┐
Provider B → register() ├── ALL register() calls run first
Provider C → register() ┘
↓
Provider A → boot() ┐
Provider B → boot() ├── ALL boot() calls run after
Provider C → boot() ┘
⚠️ Common mistake: calling another service inside register(). If that service hasn't been registered yet, you'll get a "not found in container" error. Move cross-service logic to boot().
Anatomy of a Service Provider
<?php
namespace App\Providers;
use App\Services\Riak\Connection;
use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider
{
/**
* PHASE 1 — bind into container only
* Called before any boot() method runs.
*/
public function register(): void
{
$this->app->singleton(Connection::class, function ($app) {
return new Connection(config('riak'));
});
}
/**
* PHASE 2 — configure app behavior
* All services are now registered and safe to use.
*/
public function boot(): void
{
// View composers, event listeners, model observers, macros...
}
}
// Key property available in both methods:
// $this->app → the Service Container itself
Shorthand – $bindings & $singletons Properties
For simple interface → implementation mappings with no custom closure logic, declare them
as class properties instead of writing a register() method. Laravel reads these automatically.
<?php
class AppServiceProvider extends ServiceProvider
{
// Registered as bind() calls
public $bindings = [
ServerProvider::class => DigitalOceanServerProvider::class,
];
// Registered as singleton() calls
public $singletons = [
DowntimeNotifier::class => PingdomDowntimeNotifier::class,
ServerProvider::class => ServerToolsProvider::class,
];
}
// When to use properties vs register():
// Properties → simple class-to-class bindings, no extra setup
// register() → need closures, config values, or sub-dependencies
Boot Method Dependency Injection
The boot() method supports full type-hinted dependency injection — exactly like
controllers. The container resolves and injects whatever you declare.
public function boot(ResponseFactory $response): void
{
// Add a custom macro to the response factory
$response->macro('serialized', function (mixed $value) {
return response()->json(['data' => serialize($value)]);
});
}
// This works because by the time boot() runs,
// everything has been registered into the container.
Registering Providers
All providers are listed in bootstrap/providers.php:
<?php
// bootstrap/providers.php
return [
App\Providers\AppServiceProvider::class,
App\Providers\ComposerServiceProvider::class,
App\Providers\PaymentServiceProvider::class,
];
Auto-Registration with Artisan
php artisan make:provider MyServiceProvider
# Creates app/Providers/MyServiceProvider.php
# AND automatically adds it to bootstrap/providers.php
If you create a provider class by hand, add it to providers.php yourself.
Deferred Providers – Performance Optimization
By default, every provider in bootstrap/providers.php is loaded on every request —
even if its services are never used. Deferred providers fix this.
A deferred provider tells Laravel: "only load me when someone actually asks
for my service." Laravel caches the deferred service map and loads the provider on demand.
<?php
namespace App\Providers;
use App\Services\Riak\Connection;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider implements DeferrableProvider
{
public function register(): void
{
$this->app->singleton(Connection::class, function ($app) {
return new Connection($app['config']['riak']);
});
}
// Tell Laravel exactly what this provider offers
public function provides(): array
{
return [Connection::class];
}
}
What Happens Internally
App boots
→ Laravel reads provides() from all deferred providers
→ Stores map: "Connection::class → RiakServiceProvider"
→ Does NOT load RiakServiceProvider yet ← performance win
Someone calls app(Connection::class) or type-hints Connection
→ Laravel checks its deferred map
→ Loads RiakServiceProvider NOW (register + boot)
→ Returns the Connection instance
When to Defer
✅ Defer when: provider ONLY registers container bindings
✅ Defer when: service is not needed on every request
(e.g. Riak, Stripe, specialized integrations)
❌ Don't defer if: boot() registers global event listeners
❌ Don't defer if: boot() has side effects needed on every request
(route registration, middleware, global observers)
What Goes in Each Provider
register() — Container Bindings Only
public function register(): void
{
$this->app->bind(Interface::class, Implementation::class);
$this->app->singleton(Cache::class, fn($app) => new Cache($app['config']));
$this->app->instance('key', $prebuiltObject);
}
boot() — Everything Else
public function boot(): void
{
// View composers
View::composer('profile', ProfileComposer::class);
// Event listeners
Event::listen(UserRegistered::class, SendWelcomeEmail::class);
// Model observers
User::observe(UserObserver::class);
// Custom validation rules
Validator::extend('uppercase', fn($attr, $val) => strtoupper($val) === $val);
// Route macros
Route::macro('apiResource', function (...) { /* ... */ });
// Blade directives
Blade::directive('datetime', fn($exp) => "<?php echo $exp->format('Y-m-d'); ?>");
// Gates and policies
Gate::define('update-post', fn($user, $post) => $user->id === $post->user_id);
// Response macros
Response::macro('api', fn($data) => response()->json(['data' => $data]));
}
Built-in Laravel Service Providers
Every feature of the framework is bootstrapped through a provider:
Provider What it bootstraps
────────────────────────────── ──────────────────────────────────────────
AuthServiceProvider Gates, policies, auth guards
EventServiceProvider Event → Listener mappings
RouteServiceProvider Route files (web.php, api.php), rate limiting
DatabaseServiceProvider Eloquent, DB connections, query builder
QueueServiceProvider Queue connections, workers, job dispatch
MailServiceProvider Mailer, transport drivers (SMTP, SES, etc.)
CacheServiceProvider Cache drivers and repository
FilesystemServiceProvider Storage disks, Flysystem adapters
BroadcastServiceProvider WebSocket broadcasting channels
ValidationServiceProvider Validator factory and rules
Typical Project Provider Structure
app/Providers/
├── AppServiceProvider.php ← General bindings, misc boot logic
├── AuthServiceProvider.php ← Gates and policies
├── EventServiceProvider.php ← Event → Listener mappings
├── RouteServiceProvider.php ← Route loading, rate limiters
└── (your custom providers)
├── PaymentServiceProvider.php
├── NotificationServiceProvider.php
└── ReportingServiceProvider.php
Pro structure for large apps: create one provider per domain area or feature cluster.
Keep AppServiceProvider lean — it shouldn't do everything.
Create a Provider – Full Workflow
Step 1 — Generate it
php artisan make:provider PaymentServiceProvider
# Creates app/Providers/PaymentServiceProvider.php
# Adds it to bootstrap/providers.php automatically
Step 2 — Write register() (bind services)
public function register(): void
{
$this->app->singleton(PaymentGateway::class, function ($app) {
return new StripeGateway(config('services.stripe.secret'));
});
}
Step 3 — Write boot() (configure behavior)
public function boot(): void
{
Event::listen(PaymentFailed::class, NotifyAdminListener::class);
User::observe(UserObserver::class);
}
Step 4 — Optionally defer it
// Add if this provider only registers bindings and
// the service isn't needed on every request
class PaymentServiceProvider extends ServiceProvider implements DeferrableProvider
{
public function provides(): array
{
return [PaymentGateway::class];
}
}
Visual – Full Boot Sequence With Providers
Request arrives
│
▼
bootstrap/app.php → creates Application (Service Container)
│
▼
HTTP Kernel bootstrappers run:
│
├── 1. LoadEnvironmentVariables → reads .env
├── 2. LoadConfiguration → loads config/ files
├── 3. HandleExceptions → registers error handlers
│
├── 4. RegisterProviders
│ → reads bootstrap/providers.php
│ → instantiates each provider
│ → calls register() on each ← PHASE 1
│
└── 5. BootProviders
→ calls boot() on each ← PHASE 2
│
▼
Middleware Stack → Router → Controller → Response
Common Mistakes
❌ Using a service inside register():
public function register(): void {
// WRONG — EventDispatcher may not be registered yet
Event::listen(...);
}
❌ Forgetting to add provider to bootstrap/providers.php:
Your bindings silently don't exist.
Use make:provider to avoid this.
❌ Putting everything in AppServiceProvider:
Fine for small apps. For large apps, split by domain.
❌ Deferring a provider that registers event listeners in boot():
If deferred, boot() only runs when the service is resolved —
your listeners won't fire if nothing resolves the service first.
❌ Calling DB or external services inside register():
Config is available, but the DB connection and other services
may not be. Move cross-service logic to boot().
Pro Developer Insights
1. Service providers ARE your application's architecture manifest
Reading all your providers tells you exactly what your app does, what interfaces are mapped
to what implementations, and what behavior is registered. It's the best overview of any Laravel app.
2. Keep register() dumb and boot() expressive
register() should be a mechanical list of container bindings.
boot() is where personality lives: observers, macros, composers, listeners.
3. Defer anything not needed every request
Improves boot time for free. A deferred provider for an infrequently-used integration
(Riak, Stripe, S3 processing) costs nothing to add and gives measurable boot savings.
4. Use the $bindings / $singletons shorthand
If your register() is just a list of bind/singleton calls with no closures,
replace the whole method with property arrays. Much cleaner, same result.
5. Package developers must use service providers
Packages expose their features to host apps via a provider. Add auto-discovery to skip
the manual registration step for users:
// In your package's composer.json:
"extra": {
"laravel": {
"providers": ["VendorName\\PackageName\\PackageServiceProvider"]
}
}
// Laravel will automatically register it — no manual providers.php edit needed.
Conclusion
Service Providers are the backbone of Laravel's bootstrapping process. Every feature —
framework or custom — enters the application through a provider.
register() binds things into the container — nothing else. Never use other services here.
boot() configures app behavior — event listeners, observers, macros, view composers.
All providers listed in bootstrap/providers.php. Use make:provider to auto-add.
Use the $bindings / $singletons shorthand for simple interface mappings.
Defer providers that only register bindings for infrequently-used services.
Split large apps into domain-specific providers — keep AppServiceProvider lean.