> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Integrating package caches with optimize

> Use Laravel 13's optimizes() to integrate your package's own cache generation and clearing into deployments. Learn about registration keys, exclusions, execution order, and exit codes on failure, based on the implementation.

If your package pre-generates its own metadata, simply asking users to add a dedicated command to their deployment steps makes it easy for that step to be missed during updates. With `ServiceProvider::optimizes()`, you can hook your generation and clearing commands into Laravel's `optimize` and `optimize:clear`.

This page assumes you are familiar with the [package development basics](/en/advanced/package-development) and examines the implementation in Laravel Framework `v13.35.0`. It covers the contract for registration and operation, not the format of cache files.

## Separate command registration from task registration

`commands()` registers command classes that can be called from Artisan. `optimizes()` is a separate step that registers the names of already executable commands as optimization tasks. Calling only the latter does not register the command classes.

The following example assumes your package already implements `CacheMetadataCommand` and `ClearMetadataCommand`, with `$signature` values of `courier:cache` and `courier:clear-cache` respectively.

```php theme={null}
<?php

namespace Acme\Courier;

use Acme\Courier\Console\Commands\CacheMetadataCommand;
use Acme\Courier\Console\Commands\ClearMetadataCommand;
use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        if ($this->app->runningInConsole()) {
            $this->commands([
                CacheMetadataCommand::class,
                ClearMetadataCommand::class,
            ]);

            $this->optimizes(
                optimize: 'courier:cache',
                clear: 'courier:clear-cache',
                key: 'acme-courier',
            );
        }
    }
}
```

All arguments to `optimizes()` are nullable, so you can register only generation or only clearing. However, always tell your users which step invalidates the cache you generate.

## The registration key is also a user-facing contract

`ServiceProvider` stores generation commands in the static array `$optimizeCommands` and clearing commands in `$optimizeClearCommands`. In both cases, `key` becomes the array key.

If you omit `key`, a name is derived from the provider's class name. For example, `CourierServiceProvider` becomes `courier`. Because only the class name is used, providers with the same name in different namespaces can collide.

If you register again under the same key, the command for that side is overwritten by the later value. To register multiple tasks, specify a different key for each. Also avoid the keys of Laravel's built-in tasks, such as `config` and `routes`, because identical string keys are overwritten when the built-in tasks and package tasks are combined.

<Tip>
  Explicitly specify a key that identifies your package, such as `acme-courier`, and keep it stable across releases. The key becomes the task's display name and is also the value users pass to `--except`.
</Tip>

## Package tasks run after the built-in tasks

In the implementation examined here, both commands spread the package registration array into the built-in task array and then call each task in order. As long as you use a non-colliding key, package tasks are appended after the built-in tasks.

| Command | Built-in task order | Then runs |
| - | - | - |
| `optimize` | `config:cache` → `event:cache` → `route:cache` → `view:cache` | Registered generation commands |
| `optimize:clear` | `config:clear` → `cache:clear` → `clear-compiled` → `event:clear` → `route:clear` → `view:clear` | Registered clearing commands |

```mermaid theme={null}
flowchart TD
    A["Provider boot()"] --> B["Register Artisan commands with commands()"]
    A --> C["Register key and command names with optimizes()"]
    B --> D["php artisan optimize"]
    C --> D
    D --> E["Cache built-in config, events, routes, and views"]
    E --> F["Run courier:cache"]
    C --> G["php artisan optimize:clear"]
    G --> H["Clear built-in caches"]
    H --> I["Run courier:clear-cache"]
```

Given this order, your generation command should only generate data your package owns, rather than rebuilding other built-in caches. Don't use `optimizes()` as an API for controlling dependency order between multiple packages. If you need a strict order, list the dedicated commands explicitly.

<Warning>
  `optimize:clear` also includes `cache:clear`, which deletes data in the default cache store. If you only want to clear your package's cache, run `courier:clear-cache` directly. Design your package's clearing command to delete only the keys or files it owns, without flushing the entire shared store.
</Warning>

## Exclude tasks by key or command name

The `--except` option of both commands accepts comma-separated values. Leading and trailing whitespace is trimmed from each value, and any task whose key or command name matches is excluded.

```bash theme={null}
php artisan optimize --except=acme-courier
php artisan optimize --except=courier:cache
php artisan optimize:clear --except=acme-courier,cache
```

The first two exclude the same generation task. The third excludes the package's clearing task and the built-in `cache:clear`. `cache` is a task key, not a package-specific name.

Exclusions apply only to that run. They are not a setting that disables the provider's registration or automatically deletes package caches created earlier.

## Distinguish task FAIL from the parent command's exit code

`OptimizeCommand` and `OptimizeClearCommand` call each task with `callSilently()` and pass whether the exit code is `0` to the task display. Because normal child command output is not shown, run the dedicated command directly to investigate the cause.

In Laravel `v13.35.0`, neither `handle()` method returns a child command's non-zero exit code as the parent's return value. Even if `FAIL` appears on screen, the loop continues, and if no exception is thrown, the parent command exits with `0`. Thrown exceptions, on the other hand, are rethrown by the task display component, so they don't follow the same continue-on-failure behavior.

<Warning>
  Don't treat an exit code of `0` from `php artisan optimize` alone as proof that your package cache was generated successfully. This behavior is based on the implementation of the version examined here, so check it again whenever you update your supported Laravel versions.
</Warning>

If package generation is a mandatory deployment requirement, use a procedure that lets you check the child command's exit code directly. For example, the following excludes the package task from the batch run and runs it directly once after the built-in tasks.

```bash theme={null}
php artisan optimize --except=acme-courier && php artisan courier:cache
```

This example reflects a `courier:cache` failure in the exit code, but it does not aggregate non-zero exits from built-in tasks. For deployments that must strictly detect built-in task failures too, run the required commands individually and check their exit codes.

## Design and verify caches that survive updates

Beyond registration, decide the responsibilities of your commands and the code that reads the cache.

* Generation produces the same state from the same input no matter how many times it runs, and never activates incomplete data after a partial failure.
* Clearing completes successfully even when no cache exists, and never deletes the user's published config or persistent data.
* A command that fails to generate reports an error and returns non-zero. The reading side also never treats a corrupted cache as valid unconditionally.
* When you change the cache format, tell users they need to regenerate it, and consider restarting long-running processes.

In addition to your package tests, verify the following in a consuming application. Because the registration arrays are static, also watch for registration state carrying over between tests in the same process.

| Operation to verify | Completion criteria |
| - | - |
| Run the dedicated generation and clearing commands directly | Returns `0` on success and non-zero on generation failure. Running clear twice still succeeds |
| Run `optimize` / `optimize:clear` | Each package task is called once, and the state after generation and clearing is correct |
| Specify `--except` by key and by command name | Only the targeted task is skipped |
| Make the generation command exit non-zero | You can distinguish the `FAIL` display from the parent command's exit code in the version under test |
| Regenerate after a package update | The cache is generated with the new code and config, and the old format is no longer read |

## Related pages

<Columns cols={2}>
  <Card title="Package config merging and caching" icon="sliders" href="/en/advanced/package-config-merging">
    Review how filling in published config relates to rebuilding the config cache.
  </Card>

  <Card title="Testing Laravel packages with Orchestra Testbench" icon="flask" href="/en/advanced/package-testing">
    Register providers and Artisan commands in your test environment.
  </Card>
</Columns>

## Primary sources

* [Laravel official documentation: Optimize commands](https://github.com/laravel/docs/blob/13.x/packages.md#optimize-commands)
* [ServiceProvider: optimizes() and registration keys](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Support/ServiceProvider.php)
* [OptimizeCommand: Tasks and exclusion handling](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Foundation/Console/OptimizeCommand.php)
* [OptimizeClearCommand: Clearing tasks and execution order](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Foundation/Console/OptimizeClearCommand.php)
* [Command: handle() return values and exit codes](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Console/Command.php)
* [Task: Result display and rethrowing exceptions](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Console/View/Components/Task.php)


## Related topics

- [Laravel Package Development](/en/advanced/package-development.md)
- [Advanced Topics](/en/advanced/index.md)
- [Deployment](/en/deployment.md)
- [Introducing the Blaze package](/en/blog/blaze-introduction.md)
- [Package auto-discovery internals](/en/advanced/package-discovery.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.