There are a few key points to how Harbor uses Docker that are instrumental for the integration of all the services together.

## Dynamic multi-file configuration

Docker offers a [multi-file configuration capabilities](https://docs.docker.com/compose/multiple-compose-files/) out of the box. However, it's rarely scaled beyond a few files. The main reason is that it quickly becomes non-trivial to reference multiple files dynamically for various scenarios.

Harbor solves it by using a dynamic file-matcher that revolves around the service handles (`ollama`, `webui`, `tts`, etc.). Those are matched against the docker compose files in the repo to form a set of services and related configurations to run.

It starts with a simple direct match rule, wheer compose file includes the service handle. For example:

```bash
# handle
ollama

# compose file
compose.ollama.yml
```

You can see which files are matching your config by running:

```bash
harbor defaults
# webui
# ollama

harbor cmd -h # -h - human readable output
# docker compose
#  - compose.yml
#  - compose.ollama.yml
#  - compose.webui.yml
#  - compose.x.ollama.nvidia.yml
#  - compose.x.ollama.webui.yml
```

Let's review the rules that made these files to be included.

#### Multi-service file

File name uses `OR` for default segment matching:

```bash
# Will match whenever either litellm
# langfuse or postgres handles are mentioned
compose.litellm.langfuse.postgres.yml

# Will be included for any of below launches
harbor up litellm
harbor up langfuse
harbor up postgres
```

It allows to create setups where, for example, multiple backends are all using the same DB instance.

#### Cross-service file

When there's an `.x.` in the file name, all mentioned handles are required to be present, effectively making it an `AND` rule:

```bash
# Will only match when both litellm and langfuse
# handles are mentioned
compose.x.litellm.langfuse.yml

# Match
harbor up litellm langfuse

# No match
harbor up litellm
harbor up langfuse
harbor up tts
```

This allows implementing cases where one services has a direct impact on configuration of another. For example, configuring something in Open WebUI when SearxNG is running.

#### Environment-specific files

Matched automatically, for example:
```bash
# Will be included to any "harbor up" if
# calls if the nvidia container toolkit
# and drivers are installed
compose.service.nvidia.yml
```

#### Default services

Default services are picked from the config and are always included. They can be set via the `.env` file or the CLI:

```bash
# Via .env
harbor config list | grep SERVICES_DEFAULT
# SERVICES_DEFAULT="webui ollama"

# CRUD for defaults
harbor defaults ls
harbor defaults add tts
harbor defaults rm webui

# Using "harbor config"
harbor config get services.default
# webui ollama
```

Apart from these, harbor also always mentions root `compose.yml` file, which is the base for all the services.

## Shared environment

The `.env` file is used for all the services at once. It's done for the end-user convenience at the expense of potential conflicts. When service's env is overly abstract - Harbor will prefer to use prefixed `HARBOR_` variables and add them via `environment` section in the compose file.

## Config Merging

Some services have their configs assembled at runtme from multiple files. It's useful for cases where running a service might have an impact on configuration of another service.

For example: `tts` and `webui`. When `tts` is running, `webui` should be configured to use it as a TTS backend.

```bash
harbor cmd
# -f webui.yml
# -f ollama.yml

# See sections above to learn why these
# files are included
harbor cmd tts -h
# docker compose
#  - compose.yml
#  - compose.ollama.yml
#  - compose.tts.yml
#  - compose.webui.yml
#  - compose.x.ollama.nvidia.yml
#  - compose.x.tts.nvidia.yml
#  - compose.x.webui.ollama.yml
#  - compose.x.webui.tts.yml
```

`webui.yml` comes with a default `config.json` file, that's used by the WebUI. `x.webui.tts.yml` mounts additional file to the container - `config.tts.json`, a subset of `webui`'s own configuration.

At runtime, we replace container's entrypoint with a custom one that is aware of all the optionally added volumes. It collect all configuration pieces from them and assemebles them together into a final `config.json` for the WebUI to consume.

See the [config merger util](https://github.com/av/harbor/blob/main/shared/json_config_merger.py#L113) for more details.

### Config Interpolation

Config mergers come with additional bit of functionality related to env interpolation.

#### Variable substitution

```json
{
  "key": "${HARBOR_ENV_VAR}"
}
```

Would be replaced with the value of `HARBOR_ENV_VAR` from the `.env` file.

```bash
HARBOR_ENV_VAR="value"
```

After config merging, becomes:

```json
{
  "key": "value"
}
```

#### Variable spread

```json
{
  "items": [
    "item0",
    "${...HARBOR_ENV_VAR}"
  ]
}
```

Would be replaced with the value of `HARBOR_ENV_VAR` from the `.env` file, but as a list of items.

```bash
HARBOR_ENV_VAR="item1;item2;item3"
```

After config merging, becomes:

```json
{
  "items": [
    "item0",
    "item1",
    "item2",
    "item3"
  ]
}
```