Skip to main content
Set deployment settings in .env at the root of your Egma platform checkout. Apply changes with:
Docker Compose resolves .env values and any environment variables exported by the shell. Exported values take precedence, so remove an old shell override if a change to .env has no effect. Wrap values that contain $ in single quotes in .env.

Where settings belong

Supply LiveKit and Retell connection credentials when you connect an agent. Egma stores them encrypted. They are separate from the model credentials that let your instance run personas and graders.

Model providers

Project persona settings select the language model, speech-to-text model, text-to-speech model, and voice. Project grader settings select the model used for LLM grading. Environment variables provide access to those selected providers; they do not change the selection. Runs save their selections before workers start, so queued work and retries use the same settings. The built-in persona uses OpenAI and Cartesia. A chat simulation needs its language-model provider; voice simulations also need the selected speech providers. Ensure your provider account can use the selected models. Add the required keys to .env, then run egma self-host up. These deployment credentials are not stored in PostgreSQL. The simulator receives the credentials needed for the work it claims.
The API and grader can read a provider bundle from AWS Secrets Manager. Set both EGMA_PROVIDER_CREDENTIALS_SECRET_ID and EGMA_PROVIDER_CREDENTIALS_REGION. The secret must contain a JSON object whose optional key fields are openai_api_key, deepgram_api_key, and cartesia_api_key.This selects the secret as the provider source instead of the direct environment values. Give the API and grader processes AWS credentials that can read it. The current bundle is fetched for each work item, so a rotated secret applies to subsequent work. Setting only one selector prevents startup.
Keys saved in Settings → Provider API Keys take precedence for their organization. Deployment keys or the provider bundle supply providers without an organization key; the bundle is fetched only when a work item needs it.

Public URLs and networking

Egma uses two browser-facing URLs: Both are origins: scheme, hostname, and optional port. Do not include a path, credentials, or a query string. If the instance uses HTTPS, the recording URL must use HTTPS too. The web app forwards /api/auth/*, the other account routes, /mock-tools/*, /openapi.json, and /v1/* to the API. Route the complete public instance hostname to the web app on port 3101. Keep its internal EGMA_API_ORIGIN at http://api:3100 for the bundled Compose stack.

Example with HTTPS

This example runs Caddy on the same host as Egma. Replace the two example hostnames with domains you control and point their DNS records at the server. Allow the proxy to receive HTTP and HTTPS traffic. Set these values in .env:
Use this Caddyfile:
Load the Caddy configuration and run egma self-host up. The recording proxy must preserve the original host and request path because both are used in signed recording URLs. Keep direct application ports 3100 and 3101 private through your server’s network controls. Leave the database and MinIO binds on loopback when the proxy runs on the same host. Enable EGMA_TRUST_PROXY only behind a proxy you control, with direct access to the API restricted. From another machine, verify that you can sign in at the public instance URL, run a test, and play its recording. Retell must be able to reach the instance URL for mock-tool callbacks. LiveKit workers must be able to reach it to send simulation and monitoring traces. Install the Python SDK or JavaScript SDK in each LiveKit worker. Call monitor(ctx) first in its job entrypoint for production monitoring, and await simulation(agent, ctx, session) before starting a simulation session. The simulation call is required even when a test has no mocks. Keep the worker’s SDK dependency current when updating the platform.

Ports

Change ports only when needed. If you change the web or object-store port, also change its public URL. POSTGRES_BIND, CLICKHOUSE_BIND, EGMA_S3_BIND, and EGMA_LIVEKIT_BIND default to 127.0.0.1. PostgreSQL and ClickHouse have local development passwords in Compose. Change those passwords and restrict network access before widening either database bind. For a browser on another machine, localhost points at that machine. Use a public or private network address reachable by every intended user and worker.

Authentication and email

With EGMA_SINGLE_ORGANIZATION=true, the first account claims the instance and becomes its admin. Open signup then closes. An admin invites other users from Settings → People. Set the value to false only if the deployment is intended to let people create separate organizations. For email delivery, set EGMA_SMTP_URL to your SMTP or SMTPS connection URL and EGMA_MAIL_FROM to the sender name and address. The SMTP URL contains your mail credentials; keep it private. If you omit the sender, Egma uses Egma <egma@YOUR_INSTANCE_HOSTNAME>. Reset links are not printed to platform logs. Configure SMTP before users depend on email-based account recovery. After configuration, create an invitation and confirm that the message arrives. API clients send an Egma key as a bearer token. Use a project-scoped key with write access for LiveKit monitoring. Create it through Settings → API keys or the CLI. EGMA_RATE_LIMIT_PER_MINUTE defaults to 600 authenticated requests per organization per API process. Keys in one organization share that process’s budget.

Storage

The bundled stack keeps project data in PostgreSQL, traces in ClickHouse, and objects in MinIO. It uses Docker volumes so replacing a container does not remove those files. MinIO has separate buckets for recordings and incoming production evidence. Incoming evidence is accepted into the ingestion bucket before it is written to ClickHouse; processed objects are then removed from that bucket. A single-host store depends on that host and its disks.

Bundled object storage

The public recording URL must identify this store from the browser’s network. The ingestion endpoint must identify it from the API’s network. They can point at the same store through different addresses.

External S3-compatible storage

Create the buckets and credentials in your object store first. The bundled initialization job creates buckets in MinIO only. For recordings, configure both upload and playback: Upload and playback must use the same recording bucket and region. The playback credential needs object-read permission; the uploader needs object-write permission. Use a browser-reachable storage origin for EGMA_BLOB_PUBLIC_URL, without a bucket path. Egma adds the bucket and object key when it signs the URL. For production evidence, set EGMA_INGEST_ENDPOINT, EGMA_INGEST_BUCKET, EGMA_INGEST_REGION, EGMA_INGEST_ACCESS_KEY_ID, and EGMA_INGEST_SECRET_ACCESS_KEY. Give that credential permission to put, list, read, and delete the ingestion bucket’s pending/ objects. Its endpoint must contain only a scheme, hostname, and optional port. Amazon S3 endpoints require the correct region. An absent or mismatched region can prevent startup or produce invalid request signatures. After changing storage, check a new voice recording and a new production conversation.

Generated credentials

egma self-host up writes these internal values to .egma-platform/platform.env. Do not replace them with sample values in .env. The file also records EGMA_BASE_URL. An explicit value in your operator environment updates the recorded address on the next start. Preserve the private file with PostgreSQL data. The CLI refuses to create replacement credentials when the file is missing and an existing database volume is present. If a complete set of internal credentials is stored in .env, the CLI can record those credentials without changing their values.

Retell phone simulations

Retell chat and API-based voice connections do not need a carrier route. To call a Retell phone number from a self-hosted instance, configure all four values below. Leave all four unset otherwise. For Twilio, use the termination trunk hostname and the credential-list username and password. These are different from an Account SID and Auth Token. A partial bundle is refused during startup. The bundled SIP gateway defaults to port 5060 over TCP and UDP, and RTP ports 10000 through 10020 over UDP. Set EGMA_LIVEKIT_SIP_PORT, EGMA_LIVEKIT_SIP_RTP_PORT_START, and EGMA_LIVEKIT_SIP_RTP_PORT_END if your route needs different ports. The gateway must be reachable by the carrier on its signaling and media ports. EGMA_LIVEKIT_SIP_USE_EXTERNAL_IP defaults to true. For a fixed public IP or one-to-one NAT, set EGMA_LIVEKIT_SIP_EXTERNAL_IP to the public address and configure matching port forwarding. An HTTP reverse proxy does not carry SIP or RTP traffic. Use the Retell guide to add the phone-number connection after the route is configured.

Advanced settings

The bundled values are sufficient for a local installation. Change these when you need different capacity or replace an internal service.
Direct DATABASE_URL and CLICKHOUSE_URL values configure a directly launched API process. The bundled Compose file supplies its own values; use a Compose override if you replace those databases.
Keep the ingestion log directory on persistent storage when you change its path.
EGMA_SIMULATOR_CLAIMANT and EGMA_GRADER_CLAIMANT override the process identities. Compose keeps the simulator’s report log in its named volume. A directly launched simulator can set EGMA_SIMULATOR_WAL_DIR and use EGMA_SIMULATOR_BLOB_DIR for local recordings when no S3 endpoint is set.
The optional docker-compose.workbench.yml overlay runs a fixture control plane for local simulator development. EGMA_WORKBENCH_PORT sets its host port; the default is 8085, bound to 127.0.0.1. The overlay sets EGMA_SIMULATOR_CONTROL_PLANE_URL to http://workbench:8085 inside the Compose network.Keep the workbench on loopback because its fixture routes have no authentication. The normal self-hosted setup uses the real API and does not start this service.
Egma’s own platform telemetry is off by default. It is separate from the customer traces collected through monitoring.Set EGMA_TELEMETRY=on to enable it. EGMA_TELEMETRY_OTLP_ENDPOINT selects the API and grader telemetry destination. EGMA_POSTHOG_KEY and EGMA_POSTHOG_HOST configure browser analytics and crash reporting. Rebuild through egma self-host up after changing browser telemetry settings.

Optional billing

string
The Stripe secret key that selects a billing adapter. Leave it empty unless you charge the people who use your deployment.
string
The signing secret of the Stripe webhook endpoint you point at /api/billing/stripe/webhook. Paid billing actions become available only after this signing secret is configured.
Egma records every provider request, chat simulation, web-call minute, and phone minute on every deployment, regardless of who pays. Without a Stripe secret key, charging is off. There are no plans or billing limits, allowances are unlimited, and the usage page shows your usage. Setting the key enables plans, monthly allowances, and a prepaid inference balance for every organization on the deployment. Each organization starts on the free Hobby plan with a one-time welcome credit. When billing is healthy, new work pauses if a Hobby allowance is spent. It also pauses if a required provider must use the deployment’s key and the inference balance is exhausted. A saved organization key pays for its provider independently. Pro voice usage can continue past its included minutes at the overage rate. Work already admitted finishes, and billing failures do not stop customer jobs. Open Settings → Usage and billing to see usage, the balance, and payment actions. Refusal messages link to the setting that can resolve the block. This billing setup works the same way on Egma Cloud and self-hosted deployments.

Setting Stripe up

First, create the Stripe objects needed to sell your deployment’s paid plan:
The command checks for existing objects before creating them, so you can run it again. It creates two billing meters, the paid product, its monthly fee price, and two tiered metered prices. The first tier covers the included allowance; the second sets the overage price. The command saves their identifiers in the plan row. It refuses a live key unless you set EGMA_STRIPE_SETUP_ALLOW_LIVE=1. Supply your business address to set your Stripe Tax head office and activate Stripe Tax. Provide the whole address as JSON in EGMA_STRIPE_HEAD_OFFICE, or the separate variables EGMA_STRIPE_HEAD_OFFICE_LINE1, …_LINE2, …_CITY, …_STATE, …_POSTAL_CODE and …_COUNTRY. If you omit the address, the command reports that it is missing and completes the other steps. Only the setup command reads these variables; they are not deployment settings. Second, add a webhook endpoint in Stripe pointing at https://your-deployment/api/billing/stripe/webhook, subscribed to checkout.session.completed, checkout.session.async_payment_succeeded, customer.subscription.created, customer.subscription.updated and customer.subscription.deleted. Put its signing secret in EGMA_STRIPE_WEBHOOK_SECRET and restart. Until you do, the endpoint returns 404 and payments do not credit an inference balance.

Updating pricing

Edit plan fees, allowances and overage rates in ee/src/plans.json, then deploy the updated configuration. The API loads plan values at startup and retries a failed initialization; it does not rewrite them on each settlement interval. Direct database edits are not the pricing workflow. After changing a Pro fee, included voice allowance or overage rate, rerun pnpm --filter @egma/ee stripe:setup from that release with the deployment’s DATABASE_URL and EGMA_STRIPE_SECRET_KEY. The command creates changed Stripe prices, moves their lookup keys and saves the identifiers used by new Pro Checkouts. A plan deployment alone does not update those Stripe prices. The setup command does not update existing subscriptions. Their invoices keep the prices they already use, while Egma’s plan display reads the deployed plan values. Applying a changed plan to existing subscribers therefore needs a separate rollout decision; rerunning setup does not complete that change. Model rates are in packages/db/src/models/rate-card.json. Add a new entry with a new effective date and deploy it. Keep prior entries: stored usage keeps its original price references. Model rate changes do not require Stripe setup.

Worker limits

EGMA_VOICE_SIMULATION_CONCURRENCY_CAP and EGMA_CHAT_SIMULATION_CONCURRENCY_CAP set separate deployment-wide caps on claimed or running simulations. Leave them empty for no cap. EGMA_SPEECH_PROVIDER_CONCURRENCY_CAPS sets provider caps as JSON, such as {"cartesia":2,"deepgram":150}. Work that exceeds a cap stays queued; providers absent from the JSON are uncapped. Grader workers refill each freed slot without waiting for other jobs.

Egma Cloud voice fleet

These settings select the hosted Daytona launcher. They are injected by the cloud task definition and do not belong in a self-hosted .env or Compose file. Without EGMA_VOICE_FLEET_LAUNCHER, the API does not load Daytona or STS code. The launcher gives every sandbox the shared OpenTelemetry service name egma-voice-simulator and stable release, snapshot, and runtime resource attributes. Daytona supplies the sandbox ID as service.instance.id. A sandbox claims work after it starts, so the API exports an egma.daytona.sandbox.assigned span that maps daytona.sandbox.id to egma.simulation_id. In PostHog, that mapping joins one simulation to the sandbox lifecycle and resource telemetry; the simulation ID is not a dynamic resource attribute on each Daytona metric. Daytona workers receive their room and recording authority in the claimed work order, so the launcher does not set the simulator’s LiveKit or S3 credential variables on the sandbox. The release workflow checks releaseSha before it ships the web app.