Skip to content

Commit b00a8c0

Browse files
committed
docs(operators): add self-hosting guides
1 parent 662371a commit b00a8c0

4 files changed

Lines changed: 469 additions & 0 deletions

File tree

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
---
2+
title: Docker Compose
3+
description: Run Codex Pooler with Docker Compose for a small self-hosted install.
4+
---
5+
6+
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.
7+
8+
Use it when you want a simple first install before moving to Kubernetes.
9+
10+
## Prerequisites
11+
12+
You need:
13+
14+
- Docker with Compose
15+
- `openssl`
16+
- A checked-out Codex Pooler repository
17+
18+
```bash
19+
git clone https://github.com/icoretech/codex-pooler.git
20+
cd codex-pooler
21+
```
22+
23+
## Generate the local environment
24+
25+
Create the local `.env` file before starting the stack:
26+
27+
```bash
28+
scripts/self-host/generate-env.sh
29+
```
30+
31+
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.
32+
33+
For real installs, run an explicit release image tag instead of a floating tag. The tag should identify the exact image you meant to deploy.
34+
35+
```bash
36+
CODEX_POOLER_IMAGE=ghcr.io/icoretech/codex-pooler
37+
CODEX_POOLER_IMAGE_TAG=v0.1.0
38+
```
39+
40+
## Start Codex Pooler
41+
42+
Pull and start the stack:
43+
44+
```bash
45+
docker compose pull
46+
docker compose up -d
47+
```
48+
49+
Open the local site:
50+
51+
```text
52+
http://localhost:4000
53+
```
54+
55+
On the first visit, create the owner account at `/bootstrap`. After sign-in, start with `/admin/pools`.
56+
57+
## Bootstrap runtime access
58+
59+
After the owner account exists:
60+
61+
1. Create a Pool in `/admin/pools`
62+
2. Import or connect upstream accounts in `/admin/upstreams`
63+
3. Create a Pool API key in `/admin/api-keys`
64+
4. Point clients at one of the runtime base URLs
65+
66+
```text
67+
Codex backend compatibility route:
68+
http://localhost:4000/backend-api/codex
69+
70+
OpenAI-compatible /v1 surface:
71+
http://localhost:4000/v1
72+
73+
Operator MCP endpoint:
74+
http://localhost:4000/mcp
75+
```
76+
77+
Use a Pool API key for `/backend-api` and `/v1`. Use an operator MCP token for `/mcp`.
78+
79+
For a deployed host, replace `http://localhost:4000` with `https://pooler.example.com`.
80+
81+
## Health and readiness
82+
83+
Check that the web process responds before handing traffic to clients:
84+
85+
```bash
86+
curl -fsS http://localhost:4000/healthz
87+
curl -fsS http://localhost:4000/readyz
88+
```
89+
90+
`/healthz` confirms the endpoint is alive. `/readyz` is the safer check for accepting runtime traffic.
91+
92+
You can inspect the Compose stack with:
93+
94+
```bash
95+
docker compose ps
96+
docker compose logs -f app
97+
```
98+
99+
## Stop or reset
100+
101+
Stop the stack while keeping the database volume:
102+
103+
```bash
104+
docker compose down
105+
```
106+
107+
Stop the stack and remove the local database volume:
108+
109+
```bash
110+
docker compose down -v
111+
```
112+
113+
Only use `down -v` when you intentionally want to remove local Codex Pooler data.
114+
115+
## Compose limits
116+
117+
Docker Compose is a simple self-host flow, not a high-availability topology. It is best for a single web process and local Postgres.
118+
119+
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 keep the replica caveat in that deployment plan.
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
---
2+
title: Helm
3+
description: Deploy Codex Pooler to Kubernetes with separate release roles.
4+
---
5+
6+
The Helm chart in `charts/codex-pooler` deploys the same Codex Pooler release image into Kubernetes. It separates web traffic, background work, scheduled work, and migration work so each role can scale and restart on its own.
7+
8+
Use this page as the public shape of the chart. Keep environment-specific secrets, private image repository names, internal hostnames, and operational evidence out of values files and public docs.
9+
10+
## Chart shape
11+
12+
The chart has four release roles:
13+
14+
- `app`, serves HTTP with `OBAN_MODE=web` and `PHX_SERVER=true`
15+
- `oban.worker`, runs background jobs with `OBAN_MODE=worker`
16+
- `oban.scheduler`, runs scheduled jobs with `OBAN_MODE=scheduler`
17+
- `migrations`, runs release migrations before app rollout when enabled
18+
19+
All roles use the same image repository and tag. Only the app role serves HTTP. Worker, scheduler, and migration pods don't expose the Phoenix endpoint.
20+
21+
## Image expectations
22+
23+
Set an explicit immutable image tag for real deployments. Don't rely on `latest` outside local scaffolding or temporary testing.
24+
25+
```yaml
26+
image:
27+
repository: ghcr.io/icoretech/codex-pooler
28+
tag: v0.1.0
29+
pullPolicy: IfNotPresent
30+
```
31+
32+
The chart `appVersion` is informational. Treat `image.tag` as the deployable release version.
33+
34+
## Required secrets
35+
36+
By default, the chart expects an existing Kubernetes Secret:
37+
38+
```yaml
39+
secrets:
40+
create: false
41+
existingSecret: codex-pooler-secrets
42+
```
43+
44+
That secret must provide the release values needed before Codex Pooler can read database-managed settings:
45+
46+
- `database-url`
47+
- `secret-key-base`
48+
- `totp-encryption-key`
49+
- `totp-key-version`
50+
- `upstream-secret-key`
51+
- `upstream-secret-key-version`
52+
53+
Don't put upstream access tokens, Pool API keys, MCP tokens, cookies, `auth.json`, SMTP passwords, or raw client payloads into chart values.
54+
55+
## Host and ingress
56+
57+
Set the public host through chart values:
58+
59+
```yaml
60+
config:
61+
host: pooler.example.com
62+
63+
ingress:
64+
enabled: true
65+
hosts:
66+
- host: pooler.example.com
67+
paths:
68+
- path: /
69+
pathType: Prefix
70+
```
71+
72+
Public client examples should use `https://pooler.example.com` as the deployed base URL.
73+
74+
## Role topology
75+
76+
The default topology is conservative:
77+
78+
```yaml
79+
app:
80+
enabled: true
81+
replicaCount: 1
82+
83+
oban:
84+
worker:
85+
enabled: true
86+
replicaCount: 1
87+
scheduler:
88+
enabled: true
89+
replicaCount: 1
90+
91+
migrations:
92+
enabled: true
93+
```
94+
95+
The migration hook runs database migrations and imports the vendored OpenAI pricing feed. The scheduler keeps scheduled background work separate from request-serving pods.
96+
97+
## Websocket replica caveat
98+
99+
Keep the web app at one replica unless you intentionally configure the multi-replica websocket topology.
100+
101+
Backend websocket sessions own a live upstream websocket inside an app pod. Multiple app replicas need clustering, owner-forwarding, and the explicit unsafe topology acknowledgement:
102+
103+
```yaml
104+
clustering:
105+
enabled: true
106+
query: <headless-service-dns-name>
107+
108+
app:
109+
replicaCount: 2
110+
websocketContinuity:
111+
ownerForwarding:
112+
enabled: true
113+
allowUnsafeMultiReplica: true
114+
```
115+
116+
Only set `allowUnsafeMultiReplica: true` after you have verified the topology you are running. Without that proof, keep `app.replicaCount: 1`.
117+
118+
## Render safely before install
119+
120+
Render manifests locally before applying them:
121+
122+
```bash
123+
helm template codex-pooler ./charts/codex-pooler \
124+
--set image.repository=ghcr.io/icoretech/codex-pooler \
125+
--set image.tag=v0.1.0 \
126+
--set config.host=pooler.example.com \
127+
--set ingress.enabled=true \
128+
--set ingress.hosts[0].host=pooler.example.com
129+
```
130+
131+
Inspect the output for the expected app, worker, scheduler, migration, Secret reference, Service, and Ingress resources. The rendered app Deployment should serve HTTP. Worker, scheduler, and migration resources should not.
132+
133+
For install or upgrade, provide your production values file from a private path:
134+
135+
```bash
136+
helm upgrade --install codex-pooler ./charts/codex-pooler \
137+
--namespace codex-pooler \
138+
--create-namespace \
139+
--values ./values.production.yaml
140+
```
141+
142+
Keep `values.production.yaml` private if it contains secret names, internal DNS names, ingress details, or environment-specific settings.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
---
2+
title: Operator admin UI
3+
description: Manage Pools, upstream accounts, Pool API keys, request metadata, audit metadata, jobs, and system settings from Codex Pooler.
4+
---
5+
6+
Codex Pooler's admin UI is the browser workspace for people who run a self-hosted instance. It helps operators prepare capacity, issue client credentials, inspect routing health, and review metadata about activity.
7+
8+
The admin UI is metadata-only. Operators can see route names, Pool labels, upstream labels, model names, statuses, timings, token counts, safe error codes, and audit summaries. They don't see raw prompt text, generated content, payload contents, file contents, media contents, credentials, session material, provider secrets, or raw Pool API keys.
9+
10+
## Roles and visibility
11+
12+
The first account created during bootstrap is an instance owner. Owners can manage the whole instance, including Pools, operators, global jobs, and system settings.
13+
14+
Instance admins work inside assigned Pools. They can manage Pool-scoped upstreams, Pool API keys, request logs, and audit logs for those Pools. If an admin isn't assigned to a Pool, the Pool-scoped pages show empty states instead of global data.
15+
16+
Historical request logs and audit logs for archived or deleted Pools are owner-only. This keeps old metadata available for accountability without exposing global history to admins who no longer own that Pool.
17+
18+
## Pools
19+
20+
Pools are the main routing boundary. A Pool groups upstream account assignments, routing policy, model access, and the Pool API keys used by clients.
21+
22+
Use the Pools page to answer operator questions such as:
23+
24+
1. Which Pools exist and who can operate them
25+
2. Which upstreams are assigned to each Pool
26+
3. Whether routing policy allows new client work
27+
4. Which models and limits the Pool is expected to serve
28+
5. Whether a Pool should stay active, be paused, or be archived
29+
30+
A Pool API key represents the Pool, not a single upstream. Runtime clients send the key to `/backend-api` or the narrow OpenAI-compatible `/v1` surface, then Codex Pooler chooses an eligible upstream based on Pool policy, model support, health, limit evidence, and session continuity.
31+
32+
## Upstreams
33+
34+
An upstream is a Codex account identity or Pool assignment that Codex Pooler can route eligible work to. Operators use the upstreams page to add accounts, check readiness, pause accounts, and review account status.
35+
36+
Safe upstream metadata can include labels, assignment state, model support, quota window state, freshness state, stored account identifiers, and last observed routing status. Secret material is stored behind the upstream secret store. The UI doesn't show provider credentials after import.
37+
38+
Common upstream states mean:
39+
40+
1. `active`, the account can be considered for eligible Pool traffic
41+
2. `paused`, the account stays configured but isn't selected for new traffic
42+
3. `reauth_required`, the account needs operator attention before it can route work again
43+
4. `refreshing`, the account is updating provider credentials or quota evidence
44+
45+
## Pool API keys
46+
47+
Pool API keys are bearer credentials for runtime clients. Create one for each client, environment, or automation boundary that should use a Pool.
48+
49+
The raw key is shown only once when created or rotated. After that, operators see metadata such as label, prefix, Pool, status, policy summary, usage counters, and timestamps. Use that metadata to rotate stale keys, pause a client, or confirm which Pool a client is allowed to use.
50+
51+
Pool API keys are separate from operator browser sessions and MCP tokens. Don't use a Pool API key for admin UI sign-in or the operator MCP endpoint.
52+
53+
## Invites and operators
54+
55+
Owners can invite operators and assign Pool access. Use invites for onboarding people who need browser access, then keep ongoing permissions on the operator record.
56+
57+
Invite and operator pages should be treated as account-management surfaces. They show masked identity metadata, status, role, Pool assignments, and authentication setup state. They don't reveal passwords, temporary credentials, TOTP secrets, MCP tokens, or raw emails beyond the UI's safe display format.
58+
59+
## Request logs
60+
61+
Request logs show routing and accounting metadata for runtime traffic. They are meant for operational questions, not content review.
62+
63+
Request logs can help answer:
64+
65+
1. Which Pool received traffic
66+
2. Which route family handled the request
67+
3. Which model was requested
68+
4. Whether the request succeeded, failed, retried, or was rejected
69+
5. How long the request took
70+
6. Which upstream assignment was selected, when that metadata is visible to the operator
71+
7. Whether usage or cost reporting metadata is available
72+
73+
Request logs don't show raw prompt text, generated content, payload contents, uploaded file contents, media contents, websocket contents, credentials, session material, or raw idempotency keys.
74+
75+
## Audit logs
76+
77+
Audit logs track administrative changes and security-relevant events as metadata. They help owners and assigned admins understand who changed what, when it happened, and whether it succeeded.
78+
79+
Audit records can include actor class, masked actor identity, action, target kind, outcome, time, Pool label, request correlation metadata, and sanitized detail summaries. They don't expose raw change blobs, secret settings, raw credentials, prompt text, provider payloads, or private identifiers.
80+
81+
Use audit logs to confirm control-plane changes such as Pool edits, upstream lifecycle changes, key creation or rotation, invite changes, operator permission updates, system settings changes, and MCP gate changes.
82+
83+
## Jobs
84+
85+
The jobs page is owner-facing. It shows background work metadata so owners can see whether scheduled and queued maintenance tasks are healthy.
86+
87+
Job metadata can include worker name, queue, state, attempt count, timestamps, and safe error class. It is a status surface, not a deep recovery guide, and it doesn't reveal job args that contain secrets or raw client content.
88+
89+
Typical job areas include catalog refresh, quota evidence refresh, accounting maintenance, email delivery, and cleanup tasks. A failed or retrying job usually means an owner should check the affected product area, for example upstream quota freshness or mail delivery settings.
90+
91+
## System settings
92+
93+
System settings are owner-facing controls for instance behavior. They cover runtime limits, route admission, diagnostics, ingress trust, circuit thresholds, model metadata, pricing catalog settings, operator email, MCP service gates, metrics authentication, and SMTP delivery.
94+
95+
Secret settings are write-only. The UI may show fingerprints, key versions, status, or validation results, but not raw secret values. New settings apply to new runtime work after the settings cache reloads; in-flight requests and already-open streams keep the values they started with.

0 commit comments

Comments
 (0)