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

# Docker Compose

> Run the airmux image and Postgres with persistent named volumes.

The default `docker-compose.yml` runs one long-lived `airmux` container and Postgres. Before `airmux` starts, two short-lived
containers migrate the database and apply the shipped [model catalog](/docs/reference/models-and-providers).

Install the [`airmux` CLI](/docs/reference/cli#installation) on the machine you use to manage the instance.

Build from a checkout as shown below, or pull a published image from
`ghcr.io/michel-tricot/airmux`. Release tags use `X.Y.Z`, and `latest` moves to the most recent successful release.
Main CI also publishes tested candidates as `sha-<commit>`. For example, run `docker pull ghcr.io/michel-tricot/airmux:latest`
or replace `latest` with a release version. Use a digest for reproducible production deployment. Published images include
OCI version and source revision labels.

## Start

```bash theme={null}
cp .env.example .env
docker build -t airmux:local .
docker compose up -d --wait
airmux quickstart --url http://localhost:8080
```

The CLI reads provider keys from the checkout's `.env` and stores missing credentials through the management API. The
application container does not receive the provider-key variables from that file.

## Configure the public origin

For another local port:

```bash theme={null}
AIRMUX_PORT=9000
AIRMUX_PUBLIC_URL=http://localhost:9000
```

Set these in `.env`, restart Compose, and pass the same URL to `quickstart`. For a public deployment, terminate TLS at a
reverse proxy and set `AIRMUX_PUBLIC_URL` to the exact HTTPS origin.

The packaged proxy replaces incoming forwarding headers and uses the configured public URL for the request scheme. API
and inference responses carry `Cache-Control: no-store`, including errors. The standalone data plane also applies these
headers; SSE responses carry `no-store, no-transform`, which the packaged proxy preserves. When an external TLS proxy sits in front of
the packaged proxy, its address is the observed client address. Client-specific throttling behind that edge requires an
explicitly configured trusted-edge integration; incoming client forwarding chains are never trusted by default. Keep the
split deployment's control-plane port private to its Docker network.

Set a URL-safe `POSTGRES_PASSWORD` before the first start of a public installation. Changing the Compose variable later
does not rotate the password already stored by Postgres.

## Startup and database changes

`docker compose up` runs the migration job first and the catalog job second. The `airmux` container starts only after both
jobs succeed. On later runs, migration checks the current schema revision and the catalog job reapplies the definitions
shipped with the selected image. To diagnose a failure:

```bash theme={null}
docker compose logs migrate taxonomy
```

Restarting the `airmux` container by itself does not run either job. The container does not use development mode, so the control
plane never changes the database schema or catalog while starting; it refuses to serve when the schema is out of date and
tells you to run the migration command.

## State

| Volume   | Contents                                                                                   |
| -------- | ------------------------------------------------------------------------------------------ |
| `pgdata` | Postgres data                                                                              |
| `state`  | Runtime management key, provider secrets, gateway identity, bundle cache, and usage outbox |

`docker compose down` preserves both volumes. `docker compose down -v` deletes the complete installation.

<Warning>
  Back up both volumes before an update, and do not use `down -v` unless permanent deletion is intended.
</Warning>

## Update

Check out the intended release, take a backup, then build the image once and replace the stack:

```bash theme={null}
docker build -t airmux:local .
docker compose up -d --wait
```

The migration and catalog jobs use the same image as the serving container. The image defaults to the `airmux` role, which
starts the control plane, gateway, and webapp. The control plane verifies the migrated schema and creates its internal
gateway credential only when absent. Application processes run as UID `10001`.
Docker initializes fresh named volumes from the image's private state directories. Bind mounts and custom configured
paths must be writable by the selected container user; overriding the container user does not change the startup path.
