# Run Codex Pooler with Docker Compose

Docker Compose is the quickest self-host path for a laptop, lab server, or small single-node install. It runs the Codex Pooler release image with a local Postgres database and writes the release secrets into a private `.env` file. Release images are published for `linux/amd64` and `linux/arm64`, so an ARM server works as well as an x86 one.

Use it when you want a simple first install before moving to Kubernetes.

## Prerequisites

You need:

- Docker with Compose
- Git, if you are cloning the repository
- `openssl`
- A checked-out Codex Pooler repository

```bash
git clone https://github.com/icoretech/codex-pooler.git
cd codex-pooler
```

## Generate the local environment

For every self-hosted install, choose a tagged stable release before creating
`.env`. Find the latest tagged stable release on [GitHub Releases](https://github.com/icoretech/codex-pooler/releases). The `latest` tag follows verified stable releases and does not move backward when an older version is published again. A version tag keeps the installation reproducible.

{/* x-release-please-start-version */}

```bash
export CODEX_POOLER_IMAGE=ghcr.io/icoretech/codex-pooler
export CODEX_POOLER_IMAGE_TAG=0.10.3
scripts/self-host/generate-env.sh
```

{/* x-release-please-end */}

The generated `.env` contains release secrets, encryption roots, database settings, and image settings such as `CODEX_POOLER_IMAGE` and `CODEX_POOLER_IMAGE_TAG`. Keep it private. Don't commit it, paste it into tickets, or reuse values between public installs.

If you already generated `.env`, set `CODEX_POOLER_IMAGE_TAG` to the target
tagged stable release before `docker compose pull`. Pinning a version tag makes the install
reproducible. If port `4000` is already in use, set `CODEX_POOLER_HTTP_PORT`
before generation or edit it in `.env` before starting the stack.

## Start Codex Pooler

Pull and start the stack:

```bash
docker compose pull
docker compose up -d
```

The first run pulls the app and Postgres images, waits for Postgres health, runs
the migration container, then starts the web app.

Open the local site:

```text
http://localhost:4000
```

On the first visit, create the owner account at `/bootstrap`. After sign-in, start with `/admin/pools`.

To verify the first-run redirect from the shell:

```bash
curl -sS -D - -o /dev/null http://localhost:4000/ | grep -i '^location: /bootstrap'
curl -fsS http://localhost:4000/bootstrap/status
```

The status endpoint should return `{"status":"ok","bootstrap":"pending"}` on a
fresh database. Use `http://localhost:4000` for the default Compose stack even
if the Phoenix startup banner prints an endpoint URL such as
`https://localhost`; the Compose port mapping is the local URL to open. A
release image includes the OS timezone database used for operator timezone
display.

## Bootstrap runtime access

After the owner account exists:

1. Create a Pool in `/admin/pools`
2. Import or connect upstream accounts in `/admin/upstreams`
3. Create a Pool API key in `/admin/api-keys`
4. Point clients at one of the runtime base URLs

```text
Codex backend compatibility route:
http://localhost:4000/backend-api/codex

OpenAI-compatible /v1 surface:
http://localhost:4000/v1

Operator MCP endpoint:
http://localhost:4000/mcp
```

Use a Pool API key for `/backend-api` and `/v1`. Use an operator MCP token for `/mcp`.

For a deployed host, replace `http://localhost:4000` with `https://codex-pooler.example.com`.

## Health and readiness

Check that the web process responds before handing traffic to clients:

```bash
curl -fsS http://localhost:4000/healthz
curl -fsS http://localhost:4000/readyz
```

`/healthz` confirms the endpoint is alive. `/readyz` is the safer check for accepting runtime traffic.

You can inspect the Compose stack with:

```bash
docker compose ps
docker compose logs -f app
```

## Upgrade and run migrations

For an existing Compose install, update the release image first: set
`CODEX_POOLER_IMAGE_TAG` in `.env` to the target tagged stable release. Keep a
versioned tag to make the selected upgrade reproducible. Then pull and restart
the stack:

```bash
docker compose pull
docker compose up -d
```

The Compose file includes a one-shot `migrate` service. It waits for Postgres,
runs the release migrations, imports the bundled pricing snapshot, and exits
before the `app` service starts. Normal app boot does not run migrations by
itself, so keep upgrades on this Compose path instead of starting only the app
container with `docker run`.

If you need to run migrations manually after fixing an environment or database
issue, start the database and run the migration service once:

```bash
docker compose up -d db
docker compose run --rm migrate
docker compose up -d app
```

When a migration fails, inspect the migration container logs before restarting
traffic:

```bash
docker compose logs migrate
```

Index migrations run concurrently, so they wait for other open transactions on
the same database, such as a backup or a `psql` session left idle inside a
transaction. The migration waits up to five minutes in total for them and logs
the blocking sessions while it waits; a table lock held by another session stops
it after ten seconds. If it fails with a message that names blocking sessions,
end those sessions or let them finish, then run `docker compose run --rm migrate`
again. An interrupted index build is rebuilt on the next run.

## Restore a database backup

A `pg_dump` backup leaves out planner statistics unless it was taken with
`--statistics`, so a restored database has no column statistics until
autovacuum analyzes each table. After restoring with `psql` or `pg_restore`,
analyze the database before Codex Pooler serves traffic again:

```bash
docker compose exec db sh -c 'vacuumdb --username="$POSTGRES_USER" --dbname="$POSTGRES_DB" --analyze-in-stages'
docker compose up -d
```

## Stop or reset

Stop the stack while keeping the database volume:

```bash
docker compose down
```

Stop the stack and remove the local database volume:

```bash
docker compose down -v
```

Only use `down -v` when you intentionally want to remove local Codex Pooler data.

## Compose limits

Docker Compose is a simple self-host flow, not a high-availability topology. It is best for a single web process and local Postgres.

Backend websocket sessions keep live upstream websocket state inside the web app process. Don't scale Compose web replicas unless you also design clustering and owner-forwarding for that environment. If you need multiple web replicas, use the Helm chart and review the current chart requirements in that deployment plan.