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

# Environment Variables Reference for Egma Self-Hosting

> Complete reference for every environment variable Egma reads on startup — required secrets, optional defaults, and the commands that generate safe values.

Copy `.env.example` to `.env` before you start the platform. Most variables have working defaults that are safe to leave alone. Ten do not — the deployment refuses to start if any one of them is absent, printing the name of the missing variable rather than guessing at a value. Those ten are your deployment's own secrets and its own address: a default for any of them would be a value every reader of this repository already holds.

The sections below separate what you must supply from what you can safely leave at its default.

***

## Required variables

Set all seven of the following before running `egma self-host up`. The remaining three (`EGMA_BASE_URL`, `EGMA_LIVEKIT_API_KEY`, `EGMA_LIVEKIT_API_SECRET`) are set automatically by `egma self-host up` and do not need to be set by hand unless you are driving `docker compose` directly.

<ParamField path="EGMA_ENCRYPTION_KEY" type="string" required>
  32 random bytes encoded as 64 hex characters. Seals every provider credential and platform setting stored in the database. Back this up alongside the database — the sealed values are unrecoverable without it.

  ```bash theme={null}
  openssl rand -hex 32
  ```
</ParamField>

<ParamField path="EGMA_AUTH_SECRET" type="string" required>
  A random base64 string that signs session cookies. The API refuses to start without one.

  ```bash theme={null}
  openssl rand -base64 32
  ```
</ParamField>

<ParamField path="EGMA_SIMULATOR_SERVICE_TOKEN" type="string" required>
  The bearer token the simulator presents when claiming simulation work from the API. The API checks it on every claim request — the responses carry live provider credentials. Must start with the prefix `egma_st_` so leak-scanning tools can recognise it.

  ```bash theme={null}
  echo "egma_st_$(openssl rand -hex 32)"
  ```
</ParamField>

<ParamField path="EGMA_S3_ACCESS_KEY_ID" type="string" required>
  The MinIO root credential username. You choose this value — minimum 8 characters. This credential has full write, delete, and list access to the object store, so treat it as a secret and **change `EGMA_S3_SECRET_ACCESS_KEY` before opening the store port to a network**.
</ParamField>

<ParamField path="EGMA_S3_SECRET_ACCESS_KEY" type="string" required>
  The MinIO root credential password. You choose this value — minimum 8 characters. Paired with `EGMA_S3_ACCESS_KEY_ID` above.
</ParamField>

<Note>
  **The read credentials below are created by the deployment on first start.** The deployment's `minio-init` job creates a restricted user that can fetch one object at a time and cannot write, delete, or list. Copy both values from your startup logs the first time you run `egma self-host up`, then paste them here and restart.
</Note>

<ParamField path="EGMA_S3_READ_ACCESS_KEY_ID" type="string" required>
  The MinIO read-only credential username. Created by the deployment on first start — copy from startup logs.
</ParamField>

<ParamField path="EGMA_S3_READ_SECRET_ACCESS_KEY" type="string" required>
  The MinIO read-only credential password. Created by the deployment on first start — copy from startup logs.
</ParamField>

### Variables set automatically by `egma self-host up`

If you drive `docker compose` directly without the CLI, supply these three yourself. If you use `egma self-host up`, the CLI generates and writes them for you and they do not need to appear in `.env`.

<ParamField path="EGMA_BASE_URL" type="string" required>
  The full public URL of your instance — scheme, host, and port only. No path, query string, or trailing slash. Example: `http://localhost:3101`.

  Every agent repository that connects to this instance reads its identity from this value. If others reach your instance at `http://192.168.1.10:3101`, this must say exactly that — not `http://localhost:3101`.
</ParamField>

<ParamField path="EGMA_LIVEKIT_API_KEY" type="string" required>
  The API key used to authenticate the LiveKit media server, simulator, and SIP gateway with each other. Generated by `egma self-host up` and written to `.egma-platform/platform.env`. Do not choose this value manually.
</ParamField>

<ParamField path="EGMA_LIVEKIT_API_SECRET" type="string" required>
  The API secret paired with `EGMA_LIVEKIT_API_KEY`. Generated alongside it by `egma self-host up`.
</ParamField>

***

## Optional variables with defaults

All variables in this section have working defaults. Override them only when the default conflicts with your environment — for example, if the default ports are already in use on the host.

### PostgreSQL

<ParamField path="POSTGRES_USER" type="string" default="Egma">
  The PostgreSQL role name used by the API.
</ParamField>

<ParamField path="POSTGRES_PASSWORD" type="string" default="Egma">
  Password for `POSTGRES_USER`. Change this — and narrow or close the bind address — before exposing port 5433 to any network.
</ParamField>

<ParamField path="POSTGRES_DB" type="string" default="Egma">
  The PostgreSQL database name.
</ParamField>

<ParamField path="POSTGRES_PORT" type="number" default="5433">
  The port PostgreSQL publishes on the host. Published as 5433 (not 5432) to avoid colliding with a PostgreSQL instance already running on the machine.
</ParamField>

<ParamField path="POSTGRES_BIND" type="string" default="127.0.0.1">
  The address PostgreSQL binds to on the host. Loopback by default — this is a security decision, not an omission. The password above is printed in this repository; binding to `0.0.0.0` on a shared network hands every row in the database to whoever can route to the machine. Widen it only when something off this machine must reach the database, and change `POSTGRES_PASSWORD` at the same time.
</ParamField>

### ClickHouse

<ParamField path="CLICKHOUSE_USER" type="string" default="Egma">
  The ClickHouse user name. Holds every conversation trace your platform records.
</ParamField>

<ParamField path="CLICKHOUSE_PASSWORD" type="string" default="Egma">
  Password for `CLICKHOUSE_USER`. Same caution as `POSTGRES_PASSWORD` applies.
</ParamField>

<ParamField path="CLICKHOUSE_DB" type="string" default="Egma">
  The ClickHouse database name.
</ParamField>

<ParamField path="CLICKHOUSE_PORT" type="number" default="8124">
  The port ClickHouse publishes on the host. Published as 8124 (not 8123) to avoid collisions.
</ParamField>

<ParamField path="CLICKHOUSE_BIND" type="string" default="127.0.0.1">
  The address ClickHouse binds to on the host. Loopback by default for the same reason as `POSTGRES_BIND`.
</ParamField>

### Object storage (S3 / MinIO)

<ParamField path="EGMA_S3_BUCKET" type="string" default="egma-recordings">
  The bucket recordings land in. Created on first start. Change this only if you are pointing Egma at a bucket you already own.
</ParamField>

<ParamField path="EGMA_S3_REGION" type="string" default="">
  The AWS region included in object-store request signatures. Leave empty for MinIO, which ignores regions entirely. Set to the real region (for example, `us-east-1`) when pointing Egma at Amazon S3 — the API refuses to start on an `amazonaws.com` endpoint without one rather than silently signing everything for the wrong region.

  Both the simulator (upload) and the API (playback-link signing) read this value, so set it once here and both halves use the same region.
</ParamField>

<ParamField path="EGMA_S3_PORT" type="number" default="9000">
  The port MinIO publishes on the host. Change this when port 9000 is already taken, and update `EGMA_BLOB_PUBLIC_URL` to match.
</ParamField>

<ParamField path="EGMA_S3_BIND" type="string" default="127.0.0.1">
  The address MinIO binds to on the host. Loopback by default — what answers on this port is the store's admin surface. Set to `0.0.0.0` only when browsers that play recordings are not on this machine, and update `EGMA_BLOB_PUBLIC_URL` to the address those browsers use.
</ParamField>

<ParamField path="EGMA_BLOB_PUBLIC_URL" type="string" default="http://localhost:9000">
  The address **a browser** uses to reach the object store. This is not the address the API uses internally — confusing the two is the most common cause of `SignatureDoesNotMatch` errors on recording playback.

  Set this to the same host as `EGMA_BASE_URL`, on port 9000 (or whatever `EGMA_S3_PORT` is). If `EGMA_BASE_URL` uses `https:`, this must also use `https:` — browsers block mixed-content audio before the request is sent.
</ParamField>

### API and web ports

<ParamField path="EGMA_API_PORT" type="number" default="3100">
  The port the API publishes on the host.
</ParamField>

<ParamField path="EGMA_WEB_PORT" type="number" default="3101">
  The port the web application publishes on the host. This is the port that appears in `EGMA_BASE_URL` for a default deployment.
</ParamField>

### Access and security

<ParamField path="EGMA_SINGLE_ORGANIZATION" type="boolean" default="true">
  When `true`, the first person to sign up claims the instance and becomes its admin. Open signup closes behind them — everyone after arrives by invitation. Set to `false` only for a multi-tenant deployment that intentionally holds many separate organizations.
</ParamField>

<ParamField path="EGMA_TRUST_PROXY" type="boolean" default="false">
  Whether to trust `x-forwarded-proto` and `x-forwarded-host` headers. Set to `true` only when a reverse proxy you control (nginx, Caddy, a load balancer) sits in front of the API — otherwise any client can claim any origin.
</ParamField>

<ParamField path="EGMA_RATE_LIMIT_PER_MINUTE" type="number" default="600">
  The maximum number of authenticated requests one organization may make per minute. The budget belongs to the organization, not to a key, so rotating a key does not reset it.
</ParamField>

### Email (optional)

Email is entirely optional. Without it, invitation links are returned directly to the admin who created them, signup requires no verification, and password-reset links are written to the platform log.

<Warning>
  Before adding a second person to an instance that has no mail transport configured, be aware that password-reset links are written to the platform log. Anyone who can read that log — including a `docker compose logs` run by a colleague — can use a reset link to take over any account, including an admin account. Configure `EGMA_SMTP_URL` before granting log access to others.
</Warning>

<ParamField path="EGMA_SMTP_URL" type="string">
  An SMTP connection URL. Setting this turns on email delivery for invitations, signup verification, and password resets all at once.

  ```bash theme={null}
  # Example
  EGMA_SMTP_URL=smtp://user:password@smtp.example.com:587
  ```
</ParamField>

<ParamField path="EGMA_MAIL_FROM" type="string">
  The display name and address used in the `From` header. Optional — defaults to a bare address when omitted.

  ```bash theme={null}
  # Example
  EGMA_MAIL_FROM='Egma <egma@example.com>'
  ```
</ParamField>

***

## Default judge

These three variables seed the judge that a new project receives when it has not configured one of its own. Set all three or none — a partial judge configuration causes the API to refuse startup.

<Warning>
  The `egma self-host setup` command does not ask for these. A judge belongs to the project that chose it rather than to the deployment, so set them here in `.env` if you want every new project to have a default.
</Warning>

<ParamField path="EGMA_JUDGE_PROVIDER" type="string">
  The LLM provider to use for judging — for example, `openai`.
</ParamField>

<ParamField path="EGMA_JUDGE_MODEL" type="string">
  The model name — for example, `gpt-4o`.
</ParamField>

<ParamField path="EGMA_JUDGE_API_KEY" type="string">
  The API key for the judge provider. Sealed with `EGMA_ENCRYPTION_KEY` and written into each new project's configuration at boot. Never overwrites a judge a project has already configured.
</ParamField>

***

## Quick-start checklist

Run these commands to generate all seven required secrets at once, then paste the output into your `.env`:

```bash theme={null}
echo "EGMA_ENCRYPTION_KEY=$(openssl rand -hex 32)"
echo "EGMA_AUTH_SECRET=$(openssl rand -base64 32)"
echo "EGMA_SIMULATOR_SERVICE_TOKEN=egma_st_$(openssl rand -hex 32)"

# Choose your own values for these — minimum 8 characters each:
echo "EGMA_S3_ACCESS_KEY_ID=<your-minio-admin-user>"
echo "EGMA_S3_SECRET_ACCESS_KEY=<your-minio-admin-password>"

# After first start, copy these from startup logs:
# EGMA_S3_READ_ACCESS_KEY_ID=<from-logs>
# EGMA_S3_READ_SECRET_ACCESS_KEY=<from-logs>
```

Then start the platform:

```bash theme={null}
npx @egma/cli self-host up
```

`egma self-host up` sets `EGMA_BASE_URL`, `EGMA_LIVEKIT_API_KEY`, and `EGMA_LIVEKIT_API_SECRET` automatically.
