Skip to main content

What is the service container

Laravel’s service container is a mechanism for managing class dependencies and performing dependency injection. Dependency injection means the dependencies a class needs are “injected” into the class via the constructor, or in some cases, setter methods. Look at the following example.
In this example, the PodcastController needs to fetch podcasts from a data source like Apple Music. So we inject a service that can fetch podcasts. By injecting the service, we can easily swap in a mock (dummy implementation) of the AppleMusic service during testing.
A deep understanding of the service container is essential for building a large Laravel application. It also helps you contribute to the Laravel core itself.

Zero-configuration resolution

If a class has no dependencies or only depends on other concrete classes (not interfaces), the container does not need to be instructed on how to resolve that class. For example, you may place the following code in your routes/web.php file:
This example defines a class inside the route file for demonstration purposes. In a real application, service classes should be defined in the app/Services directory.
When you visit this route, Laravel automatically resolves the Service class and injects it into the route’s handler. You get the benefits of dependency injection without needing any configuration files. Many classes you write in a Laravel application—controllers, event listeners, middleware, and so on—automatically have their dependencies injected via the container.

Binding

Basic binding

Almost all of your bindings will be registered inside service providers. Inside a service provider, you have access to the container via the $this->app property.

bind

Use the bind method to register a binding, passing the class or interface name that we wish to register along with a closure.
The closure receives the container itself as an argument. You can use this to resolve sub-dependencies. To manipulate the container outside a service provider, use the App facade.
There is no need to bind classes into the container if they do not depend on any interfaces. The container can resolve these objects automatically using reflection.

singleton

The singleton method binds a class or interface such that it is only resolved once. Once a singleton has been resolved, the same instance is returned on subsequent calls into the container.
You may use the singletonIf method to register a singleton container binding only if a binding has not already been registered for the given type.

Singleton attribute

Alternatively, you may mark an interface or class with the #[Singleton] attribute to indicate to the container that it should be resolved one time.

Binding scoped singletons

The scoped method binds a class or interface into the container that should only be resolved one time within a given Laravel request / job lifecycle. While this method is similar to the singleton method, instances registered using the scoped method are flushed whenever the Laravel application starts a new “lifecycle”, such as when a Laravel Octane worker processes a new request or when a queue worker processes a new job.
You may use the scopedIf method to register a scoped container binding only if a binding has not already been registered for the given type.

Scoped attribute

Alternatively, you may mark an interface or class with the #[Scoped] attribute to indicate to the container that it should be resolved one time within a given Laravel request / job lifecycle.

instance

You may also bind an existing object instance into the container using the instance method. The given instance will always be returned on subsequent calls into the container.

Binding interfaces to implementations

One of the powerful features of the service container is its ability to bind an interface to a given implementation. For example, suppose we have an EventPusher interface and a RedisEventPusher implementation.
This tells the container that it should inject the RedisEventPusher when a class needs an implementation of EventPusher. Then, all you need to do is type-hint the EventPusher interface in a constructor.
By depending on an interface, you don’t need to change your code even if you swap out the implementation. This makes testing and future changes easier.

Bind attribute

Laravel also provides a Bind attribute for added convenience. You can apply this attribute to any interface to tell Laravel which implementation should be automatically injected whenever that interface is requested. When using the Bind attribute, there is no need to perform any additional service registration in your application’s service providers. In addition, multiple Bind attributes may be placed on an interface in order to configure a different implementation that should be injected for a given set of environments.
Furthermore, Singleton and Scoped attributes may be applied to indicate if the container bindings should be resolved once or once per request / job lifecycle.

Automatic resolution (DI via type hints)

When resolving classes such as controllers, event listeners, and middleware, the service container examines the constructor’s type hints and injects the dependencies automatically.
If UserRepository doesn’t depend on an interface, you don’t need to register it in the container. Just visit the following route and the container automatically resolves and injects the dependency into the controller.

Resolving from the container

The make method

Use the make method to resolve a class instance from the container.
If some of your class’s dependencies are not resolvable by the container, you can inject additional arguments using the makeWith method.

Automatic injection

In practice, you’ll rarely need to call the make method directly. Just add type hints to the constructor of a class that the container resolves (controllers, event listeners, middleware, and so on) and the container will inject them automatically.

Facades and the container

Laravel facades provide a static interface to objects in the container. For example, Cache::get() internally retrieves the Cache service from the container and calls the method.
Facades are a convenient wrapper around the container. You can also swap facades with mocks during testing.

Practical example of constructor injection

Let’s look at a typical pattern in a real application.
1

Define the interface

2

Create the implementation class

3

Bind it in a service provider

4

Receive it via injection in a controller

With this pattern, switching the payment service from Stripe to another provider only requires changing the binding in one place.

Next steps

Service providers

Learn how to register bindings using service providers.
Last modified on July 23, 2026