# Discovery

**Package:** Laravel Compose | **Version:** 1 | **URL:** https://mozex.dev/docs/laravel-compose/v1/stacks/discovery

---

The package finds stacks three ways, and they combine.

## Directory discovery

`config/compose.php` ships with two patterns:

```php
'discover' => [
    app_path('Docker'),
    base_path('Modules/*/Docker'),
],
```

Each pattern is a path or a glob. A matched directory that holds a compose file is a stack. One that doesn't is treated as a parent, and each of its child directories with a compose file is a stack. So both of these layouts work with no configuration:

```
app/Docker/Meilisearch/docker-compose.yml     plain app: app/Docker is the parent
Modules/Search/Docker/docker-compose.yml      modular app: the Docker directory is the stack
```

A module with several stacks can add `base_path('Modules/*/Docker/*')` to the list.

When a `Stack` subclass is declared by a PHP file directly in the stack directory, that class defines the stack. The lookup reads those files to find the class name (subdirectories are left alone, so a bind-mounted PHP tree is never scanned), then loads it through Composer, so the namespace has to match a PSR-4 mapping in `composer.json`. A class that can't be loaded (a namespace typo, a stale authoritative classmap) makes the directory count as class-less; `compose:doctor` warns about it by name. Two `Stack` classes in one directory is an error.

## Class-less stacks

A directory with a compose file and no class becomes a `DiscoveredStack`: named after the compose file's `name:` or the directory, enabled, with an empty environment. That's enough for a service whose every knob has a `${VAR:-default}` in the compose file, such as a Mailpit for local development.

In the module layout, give the compose file a `name:`. The fallback is the directory name, and every module's stack directory is called `Docker`, so two class-less module stacks without one would both be named `docker` and the registry would refuse the second.

A class-less stack has nothing to write, so a redeploy leaves an existing env file in that directory alone. Writing one by hand is the way to give such a stack a value, and it survives deploys as long as the file is in the release (or linked into it). The doctor reads that file too, so a `${VAR}` it defines counts as provided.

## Explicit registration

List classes in config when you'd rather not scan, or when a stack lives outside the discovery paths:

```php
'stacks' => [
    App\Docker\Meilisearch\MeilisearchStack::class,
],
```

Or register at runtime from a service provider:

```php
use Mozex\Compose\Facades\Compose;

Compose::register(MeilisearchStack::class);
Compose::register(new GatewayStack);
```

Configured and registered stacks come first. A discovered stack whose directory is already registered is skipped, so listing a class that discovery would also find doesn't duplicate it.

## Names

Every stack needs a unique name, and it has to be a valid Compose project name: lowercase letters, digits, dashes, and underscores, starting with a letter or digit. Directory names are normalized (`Meilisearch` becomes `meilisearch`, `Image Tools` becomes `image-tools`); a `name()` you override is validated as-is. Two stacks with the same name throw, naming both classes and directories.

The name is passed to every compose call as `--project-name`, along with `--project-directory` and `--file`, so two stacks that both live in a directory called `Docker` never collide.

## Reading the registry

```php
Compose::stacks();          // array<string, Stack>, keyed by name
Compose::stack('meili');    // throws ComposeException for an unknown name
Compose::has('meili');
```

---

## Table of Contents

- [Introduction](https://mozex.dev/docs/laravel-compose/v1)
- [AI Integration](https://mozex.dev/docs/laravel-compose/v1/ai-integration)
- [Support Us](https://mozex.dev/docs/laravel-compose/v1/support-us)
- [Requirements](https://mozex.dev/docs/laravel-compose/v1/requirements)
- [Changelog](https://mozex.dev/docs/laravel-compose/v1/changelog)
- [Contributing](https://mozex.dev/docs/laravel-compose/v1/contributing)
- [Questions & Issues](https://mozex.dev/docs/laravel-compose/v1/questions-and-issues)
- [About Mozex](https://mozex.dev/docs/laravel-compose/v1/about)
- [Installation](https://mozex.dev/docs/laravel-compose/v1/installation)
- [Configuration](https://mozex.dev/docs/laravel-compose/v1/configuration)

### Stacks

- [Defining a Stack](https://mozex.dev/docs/laravel-compose/v1/stacks/defining-a-stack)
- [Discovery](https://mozex.dev/docs/laravel-compose/v1/stacks/discovery)
- [Environment Files](https://mozex.dev/docs/laravel-compose/v1/stacks/environment-files)
- [Compose Files](https://mozex.dev/docs/laravel-compose/v1/stacks/compose-files)

### Deploying

- [Redeploy](https://mozex.dev/docs/laravel-compose/v1/deploying/redeploy)
- [Hosting Platforms](https://mozex.dev/docs/laravel-compose/v1/deploying/hosting-platforms)
- [Remote Daemons](https://mozex.dev/docs/laravel-compose/v1/deploying/remote-daemons)
- [Doctor](https://mozex.dev/docs/laravel-compose/v1/deploying/doctor)

### Operating

- [Status, Logs, and Down](https://mozex.dev/docs/laravel-compose/v1/operating/status-logs-down)
- [Health Check](https://mozex.dev/docs/laravel-compose/v1/operating/health-check)
- [Events](https://mozex.dev/docs/laravel-compose/v1/operating/events)
- [Testing](https://mozex.dev/docs/laravel-compose/v1/operating/testing)

### Recipes

- [Meilisearch](https://mozex.dev/docs/laravel-compose/v1/recipes/meilisearch)
- [Telegram Bot API](https://mozex.dev/docs/laravel-compose/v1/recipes/telegram-bot-api)