Laravel Package: Providers, Auto-Discovery &amp; Config | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com) [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles  ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

  [ home ](https://www.msaied.com)    [ articles ](https://www.msaied.com/articles)    Laravel Package Development: Service Providers, Auto-Discovery, and Config Merging        On this page       1. [  Why Package Architecture Matters ](#why-package-architecture-matters)
2. [  Service Provider Anatomy ](#service-provider-anatomy)
3. [  register() vs boot() ](#coderegistercode-vs-codebootcode)
4. [  Auto-Discovery via composer.json ](#auto-discovery-via-codecomposerjsoncode)
5. [  Config Merging Done Right ](#config-merging-done-right)
6. [  Deferred Providers for Heavy Bindings ](#deferred-providers-for-heavy-bindings)
7. [  Testing Your Package in Isolation ](#testing-your-package-in-isolation)
8. [  Key Takeaways ](#key-takeaways)

  ![Laravel Package Development: Service Providers, Auto-Discovery, and Config Merging](https://cdn.msaied.com/258/673a80fa8e42ae375a4bba21bdcd92ea.png)

  #laravel   #packages   #service-providers   #composer   #architecture  

 Laravel Package Development: Service Providers, Auto-Discovery, and Config Merging 
====================================================================================

     21 Jun 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Why Package Architecture Matters  ](#why-package-architecture-matters)
2. [  02   Service Provider Anatomy  ](#service-provider-anatomy)
3. [  03   register() vs boot()  ](#coderegistercode-vs-codebootcode)
4. [  04   Auto-Discovery via composer.json  ](#auto-discovery-via-codecomposerjsoncode)
5. [  05   Config Merging Done Right  ](#config-merging-done-right)
6. [  06   Deferred Providers for Heavy Bindings  ](#deferred-providers-for-heavy-bindings)
7. [  07   Testing Your Package in Isolation  ](#testing-your-package-in-isolation)
8. [  08   Key Takeaways  ](#key-takeaways)

 Why Package Architecture Matters
--------------------------------

Dropping reusable code into a `packages/` directory is easy. Shipping something that installs cleanly, respects host-app configuration, and doesn't pollute the container is hard. This article walks through the decisions that separate a throwaway internal package from one you'd confidently open-source.

---

Service Provider Anatomy
------------------------

Every package starts with a service provider. Keep it thin — registration logic only, no business logic.

```php
namespace Acme\Auditor;

use Illuminate\Support\ServiceProvider;

class AuditorServiceProvider extends ServiceProvider
{
    // Defer binding until the service is actually resolved
    public bool $defer = false;

    public function register(): void
    {
        $this->mergeConfigFrom(
            __DIR__ . '/../config/auditor.php',
            'auditor'
        );

        $this->app->singleton(AuditLogger::class, function ($app) {
            return new AuditLogger(
                $app['config']->get('auditor'),
                $app['db']
            );
        });
    }

    public function boot(): void
    {
        if ($this->app->runningInConsole()) {
            $this->publishes([
                __DIR__ . '/../config/auditor.php' => config_path('auditor.php'),
            ], 'auditor-config');

            $this->loadMigrationsFrom(__DIR__ . '/../database/migrations');
        }

        $this->loadRoutesFrom(__DIR__ . '/../routes/auditor.php');
    }
}

```

### `register()` vs `boot()`

- **`register()`** — bind things into the container. No facades, no other services. Other providers may not be loaded yet.
- **`boot()`** — everything else: routes, views, migrations, event listeners. All providers have been registered by this point.

Violating this order is the single most common cause of "service not found" errors in packages.

---

Auto-Discovery via `composer.json`
----------------------------------

Laravel reads the `extra.laravel` key to register providers and aliases automatically — no manual `config/app.php` edits needed.

```json
{
  "extra": {
    "laravel": {
      "providers": [
        "Acme\\Auditor\\AuditorServiceProvider"
      ],
      "aliases": {
        "Auditor": "Acme\\Auditor\\Facades\\Auditor"
      }
    }
  }
}

```

Host apps can opt out per-package in their own `composer.json`:

```json
{
  "extra": {
    "laravel": {
      "dont-discover": ["acme/auditor"]
    }
  }
}

```

This is important for packages that should be explicitly configured before loading.

---

Config Merging Done Right
-------------------------

`mergeConfigFrom` performs a **shallow** merge. Nested arrays in the host app's published config will be completely replaced by the package default if the key exists at the top level. This surprises most developers.

```php
// Package default
[
  'driver' => 'database',
  'channels' => ['slack', 'log'],
]

// Host app publishes and sets only:
[
  'driver' => 'redis',
]

// Result after mergeConfigFrom — 'channels' is MISSING
// because the host key 'auditor' exists, so no merge happens

```

For deep merges, do it manually in `register()`:

```php
public function register(): void
{
    $default = require __DIR__ . '/../config/auditor.php';
    $app = $this->app['config']->get('auditor', []);

    $this->app['config']->set(
        'auditor',
        array_replace_recursive($default, $app)
    );
}

```

---

Deferred Providers for Heavy Bindings
-------------------------------------

If your package registers a service that isn't needed on every request, defer it:

```php
use Illuminate\Contracts\Support\DeferrableProvider;

class AuditorServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function provides(): array
    {
        return [AuditLogger::class];
    }

    public function register(): void
    {
        $this->app->singleton(AuditLogger::class, ...);
    }
}

```

Laravel caches the `provides()` list and only boots this provider when `AuditLogger` is actually resolved. Don't defer providers that register routes or listeners — those must run on every request.

---

Testing Your Package in Isolation
---------------------------------

Use `orchestra/testbench` to bootstrap a minimal Laravel app inside your test suite:

```php
use Orchestra\Testbench\TestCase;

class AuditLoggerTest extends TestCase
{
    protected function getPackageProviders($app): array
    {
        return [AuditorServiceProvider::class];
    }

    protected function defineEnvironment($app): void
    {
        $app['config']->set('auditor.driver', 'array');
    }

    public function test_logger_resolves_from_container(): void
    {
        $logger = $this->app->make(AuditLogger::class);
        $this->assertInstanceOf(AuditLogger::class, $logger);
    }
}

```

---

Key Takeaways
-------------

- Keep `register()` for container bindings only; use `boot()` for everything that touches other services.
- `mergeConfigFrom` is shallow — implement `array_replace_recursive` for nested config safety.
- Auto-discovery via `extra.laravel` removes friction but always document the opt-out path.
- Implement `DeferrableProvider` for heavy services not needed on every request.
- Use `orchestra/testbench` to test your provider lifecycle without a full app install.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Flaravel-package-development-service-providers-auto-discovery-and-config-merging-1&text=Laravel+Package+Development%3A+Service+Providers%2C+Auto-Discovery%2C+and+Config+Merging) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Flaravel-package-development-service-providers-auto-discovery-and-config-merging-1) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  Why does my published config override get ignored after mergeConfigFrom?        mergeConfigFrom only fills in missing top-level keys. If the host app has already set the top-level key (even partially), the package defaults for nested keys are dropped. Use array_replace_recursive in register() for a true deep merge. 

      Q02  Should I always use auto-discovery for my package?        Auto-discovery is convenient for general-purpose packages. If your package requires explicit configuration before the provider boots — such as credentials or a driver choice — consider documenting manual registration so developers aren't surprised by a misconfigured singleton on first install. 

      Q03  When should a provider be deferred?        Defer a provider when it only registers container bindings that aren't needed on every request — background services, report generators, or third-party API clients. Never defer providers that register routes, middleware, or event listeners, as those must be available from the first request. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://www.msaied.com/articles) 

 [ ![Read/Write Splitting, Connection Pooling, and Sticky Reads in Laravel](https://cdn.msaied.com/295/d977bd189583149245c03d6d763d9db5.png) laravel database performance 

### Read/Write Splitting, Connection Pooling, and Sticky Reads in Laravel

Learn how Laravel's database layer handles read/write splitting, when sticky reads matter, and how to layer Pg...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 26 Jun 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/readwrite-splitting-connection-pooling-and-sticky-reads-in-laravel-2) [ ![Laravel Service Container: Contextual Binding, Tagging, and Method Injection](https://cdn.msaied.com/294/e5b9d047bd33c3f8b80069ef6a178884.png) laravel service-container dependency-injection 

### Laravel Service Container: Contextual Binding, Tagging, and Method Injection

Go beyond basic binding. Learn how contextual binding resolves different implementations per consumer, how tag...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 26 Jun 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-service-container-contextual-binding-tagging-and-method-injection-1) [ ![PostgreSQL JSONB in Laravel: Indexing, Querying, and Casting Without the Pain](https://cdn.msaied.com/293/f392a5ea52536901eac9677ffa2d070d.png) laravel postgresql eloquent 

### PostgreSQL JSONB in Laravel: Indexing, Querying, and Casting Without the Pain

JSONB columns unlock flexible schemas, but most Laravel apps leave performance on the table. Learn how to inde...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 25 Jun 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/postgresql-jsonb-in-laravel-indexing-querying-and-casting-without-the-pain) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://www.msaied.com)
- [Projects](https://www.msaied.com/projects)
- [Articles](https://www.msaied.com/articles)
- [Certificates](https://www.msaied.com/certificates)
- [Contact](https://www.msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
