# Environment variables

> Authoritative Config struct and .env.example: core, Docker, server, providers, embeddings, summarizer, search, OAuth, proxy, supervision, Graphiti, Langfuse, and DB pool keys with defaults.

- Repository: vxcontrol/pentagi
- GitHub: https://github.com/vxcontrol/pentagi
- Human docs: https://grok-wiki.com/public/docs/vxcontrol-pentagi-eca0cf4de5f5
- Complete Markdown: https://grok-wiki.com/public/docs/vxcontrol-pentagi-eca0cf4de5f5/llms-full.txt

## Source Files

- `backend/pkg/config/config.go`
- `.env.example`
- `backend/docs/config.md`
- `docker-compose.yml`
- `backend/pkg/config/config_test.go`

---

---
title: "Environment variables"
description: "Authoritative Config struct and .env.example: core, Docker, server, providers, embeddings, summarizer, search, OAuth, proxy, supervision, Graphiti, Langfuse, and DB pool keys with defaults."
---

PentAGI loads runtime configuration from process environment variables (optional `.env` via `godotenv`) into `config.Config` in `backend/pkg/config/config.go`. `NewConfig()` parses fields with `github.com/caarlos0/env/v10`, applies `envDefault` values when unset, then ensures `INSTALLATION_ID` and validates `LICENSE_KEY`. Compose deployments also use host-side keys in `.env.example` (for example `PENTAGI_*`, Postgres credentials, Langfuse stack) that docker-compose interpolates into container env and volumes—those keys are not all fields on `Config`.

<Info>
Defaults below come from `env` struct tags on `Config`. Empty means no default (must be set for the feature). Docker Compose may override some app defaults (for example `SERVER_PORT=8443`, `SERVER_USE_SSL=true`, `DOCKER_INSIDE=true` in `.env.example`).
</Info>

## Load path and ownership

```text
.env / process env
        │
        ▼
 NewConfig()  ── godotenv.Load (ignore missing)
        │
        ▼
 env.Parse → Config
        │
        ├── ensureInstallationID  (UUID → $DATA_DIR/installation_id)
        └── ensureLicenseKey      (SDK introspect; invalid → clear)
        │
        ▼
 main / providers / tools / docker / server
```

| Surface | Role |
| --- | --- |
| `config.Config` | Authoritative application settings (`env:"..."` tags) |
| `.env.example` | Template for compose + app env |
| `docker-compose.yml` | Maps host `.env` into `pentagi` service env/volumes |
| UI Settings | Provider profiles, prompts, API tokens—not LLM credentials or integration secrets |

`PgxPool` on `Config` is populated at runtime after pool creation; it is tagged `env:"-"` and is not an environment variable.

## Minimal deploy set

<Steps>
  <Step title="Copy template">
    `cp .env.example .env` from the repository root.
  </Step>
  <Step title="Set at least one LLM credential">
    Example: `OPEN_AI_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, Bedrock auth, or `LLM_SERVER_*` / `OLLAMA_SERVER_*`. Without a reachable provider, flows cannot run.
  </Step>
  <Step title="Harden secrets">
    Change `COOKIE_SIGNING_SALT`, `PENTAGI_POSTGRES_PASSWORD`, and any stack passwords before production.
  </Step>
  <Step title="Align public URL and CORS">
    For compose: `PUBLIC_URL=https://localhost:8443`, `CORS_ORIGINS=https://localhost:8443` (or your real origin).
  </Step>
  <Step title="Start stack">
    `docker compose up -d` then open `https://localhost:8443`. Compose builds `DATABASE_URL` from `PENTAGI_POSTGRES_*`.
  </Step>
</Steps>

## Core and cloud

| Variable | Type | Default | Description |
| --- | --- | --- | --- |
| `DATABASE_URL` | string | `postgres://pentagiuser:pentagipass@pgvector:5432/pentagidb?sslmode=disable` | Postgres + pgvector DSN. Compose overrides with `PENTAGI_POSTGRES_*` |
| `DEBUG` | bool | `false` | Extra logging / debug paths |
| `DATA_DIR` | string | `./data` | Persistent data (installation id, volumes, screenshots) |
| `ASK_USER` | bool | `false` | Require user confirmation for sensitive tool operations |
| `INSTALLATION_ID` | string | *(auto UUID)* | Cloud install id; if missing/invalid, written under `$DATA_DIR/installation_id` |
| `LICENSE_KEY` | string | *(none)* | Cloud license; invalid keys are cleared after introspect |

## Database connection pools

| Variable | Type | Default | Description |
| --- | --- | --- | --- |
| `DATABASE_MAX_OPEN_CONNS` | int | `25` | Max open `sql.DB` connections (sqlc + GORM share one pool) |
| `DATABASE_MAX_IDLE_CONNS` | int | `5` | Max idle `sql.DB` connections |
| `DATABASE_VECTOR_MAX_CONNS` | int | `10` | Max connections in shared `pgxpool` for pgvector stores |

Compose host credentials (not on `Config`): `PENTAGI_POSTGRES_USER` (default `postgres`), `PENTAGI_POSTGRES_PASSWORD` (default `postgres`), `PENTAGI_POSTGRES_DB` (default `pentagidb`).

## Docker and terminal sandbox

| Variable | Type | Default | Description |
| --- | --- | --- | --- |
| `DOCKER_INSIDE` | bool | `false` | PentAGI runs in Docker and must use host Docker via socket |
| `DOCKER_NET_ADMIN` | bool | `false` | Grant `NET_ADMIN` to primary terminal container |
| `DOCKER_SOCKET` | string | *(none)* | Docker socket path on host |
| `DOCKER_NETWORK` | string | *(none)* | Bridge network name, or `host` for host networking |
| `DOCKER_PUBLIC_IP` | string | `0.0.0.0` | Host IP for port bindings (bridge mode) |
| `DOCKER_WORK_DIR` | string | *(none)* | Custom work dir mapping for containers |
| `DOCKER_DEFAULT_IMAGE` | string | `debian:latest` | Fallback general image |
| `DOCKER_DEFAULT_IMAGE_FOR_PENTEST` | string | `vxcontrol/kali-linux` | Default pentest image |
| `TERMINAL_TOOL_TIMEOUT` | int | `1200` | Default terminal timeout (seconds) when agent passes `timeout<=0`. Clamped to `1`–`10800` (over-range → 10800) |

Docker SDK client env (compose / daemon, not `Config` fields): `DOCKER_HOST`, `DOCKER_TLS_VERIFY`, `DOCKER_CERT_PATH`.

Compose volume helpers: `PENTAGI_DATA_DIR`, `PENTAGI_SSL_DIR`, `PENTAGI_OLLAMA_DIR`, `PENTAGI_DOCKER_SOCKET`, `PENTAGI_DOCKER_CERT_PATH`, `PENTAGI_LLM_SERVER_CONFIG_PATH`, `PENTAGI_OLLAMA_SERVER_CONFIG_PATH`, `PENTAGI_LISTEN_IP`, `PENTAGI_LISTEN_PORT`, `PENTAGI_IMAGE`.

## HTTP server and frontend

| Variable | Type | Default | Description |
| --- | --- | --- | --- |
| `SERVER_PORT` | int | `8080` | Listen port (compose example often `8443`) |
| `SERVER_HOST` | string | `0.0.0.0` | Bind address |
| `SERVER_USE_SSL` | bool | `false` | TLS listen when cert/key set (compose often `true`) |
| `SERVER_SSL_KEY` | string | *(none)* | TLS key path |
| `SERVER_SSL_CRT` | string | *(none)* | TLS certificate path |
| `STATIC_DIR` | string | `./fe` | Local SPA assets when not reverse-proxying |
| `STATIC_URL` | URL | *(none)* | External static origin; enables reverse-proxy static mode |
| `CORS_ORIGINS` | string list | `*` | Allowed origins (comma-separated). Non-`*` enables credentials |

## Authentication and OAuth

| Variable | Type | Default | Description |
| --- | --- | --- | --- |
| `COOKIE_SIGNING_SALT` | string | *(none)* | Session cookie store key material |
| `PUBLIC_URL` | string | `""` | Public origin for OAuth callbacks |
| `OAUTH_GOOGLE_CLIENT_ID` | string | *(none)* | Google OAuth client id |
| `OAUTH_GOOGLE_CLIENT_SECRET` | string | *(none)* | Google OAuth client secret |
| `OAUTH_GITHUB_CLIENT_ID` | string | *(none)* | GitHub OAuth client id |
| `OAUTH_GITHUB_CLIENT_SECRET` | string | *(none)* | GitHub OAuth client secret |

OAuth registers only when `PUBLIC_URL` parses and the provider pair is non-empty. Provider console redirect URI must be:

```text
${PUBLIC_URL}/api/v1/auth/login-callback
```

## Scraper

| Variable | Type | Default | Description |
| --- | --- | --- | --- |
| `SCRAPER_PUBLIC_URL` | string | *(none)* | Client-facing scraper base URL |
| `SCRAPER_PRIVATE_URL` | string | *(none)* | Backend→scraper URL (compose often basic-auth to `scraper`) |

Compose-only scraper service keys: `LOCAL_SCRAPER_USERNAME`, `LOCAL_SCRAPER_PASSWORD`, `LOCAL_SCRAPER_MAX_CONCURRENT_SESSIONS`, `SCRAPER_LISTEN_IP`, `SCRAPER_LISTEN_PORT`.

## LLM providers (BYOK)

At least one configured provider is required for agent work. Keys stay server-managed; the UI manages per-agent model profiles on top of these endpoints.

### OpenAI and Anthropic

| Variable | Default |
| --- | --- |
| `OPEN_AI_KEY` | *(none)* |
| `OPEN_AI_SERVER_URL` | `https://api.openai.com/v1` |
| `ANTHROPIC_API_KEY` | *(none)* |
| `ANTHROPIC_SERVER_URL` | `https://api.anthropic.com/v1` |

### Gemini

| Variable | Default |
| --- | --- |
| `GEMINI_API_KEY` | *(none)* |
| `GEMINI_SERVER_URL` | `https://generativelanguage.googleapis.com` |

### AWS Bedrock

| Variable | Default | Notes |
| --- | --- | --- |
| `BEDROCK_REGION` | `us-east-1` | AWS region |
| `BEDROCK_DEFAULT_AUTH` | `false` | SDK default credential chain (highest priority when true) |
| `BEDROCK_BEARER_TOKEN` | *(none)* | Bearer auth over static keys |
| `BEDROCK_ACCESS_KEY_ID` | *(none)* | Static access key |
| `BEDROCK_SECRET_ACCESS_KEY` | *(none)* | Static secret |
| `BEDROCK_SESSION_TOKEN` | *(none)* | Optional STS session token |
| `BEDROCK_SERVER_URL` | *(none)* | Custom / VPC endpoint |

Auth priority: `BEDROCK_DEFAULT_AUTH` → bearer token → access key + secret.

### DeepSeek, GLM, Kimi, Qwen

| Provider | Key | Server URL default | Optional LiteLLM prefix |
| --- | --- | --- | --- |
| DeepSeek | `DEEPSEEK_API_KEY` | `https://api.deepseek.com` | `DEEPSEEK_PROVIDER` |
| GLM | `GLM_API_KEY` | `https://api.z.ai/api/paas/v4` | `GLM_PROVIDER` |
| Kimi | `KIMI_API_KEY` | `https://api.moonshot.ai/v1` | `KIMI_PROVIDER` |
| Qwen | `QWEN_API_KEY` | `https://dashscope-us.aliyuncs.com/compatible-mode/v1` | `QWEN_PROVIDER` |

Regional alternate endpoints are documented in configure-llm-providers; only the defaults above live in `Config`.

### Custom OpenAI-compatible (`LLM_SERVER_*`)

| Variable | Default | Description |
| --- | --- | --- |
| `LLM_SERVER_URL` | *(none)* | Base URL |
| `LLM_SERVER_KEY` | *(none)* | API key |
| `LLM_SERVER_MODEL` | *(none)* | Default model id |
| `LLM_SERVER_PROVIDER` | *(none)* | Model name prefix (e.g. LiteLLM) |
| `LLM_SERVER_CONFIG_PATH` | *(none)* | Per-agent YAML config path |
| `LLM_SERVER_LEGACY_REASONING` | `false` | Legacy reasoning payload format |
| `LLM_SERVER_PRESERVE_REASONING` | `false` | Keep reasoning content across turns |

### Ollama

| Variable | Default | Description |
| --- | --- | --- |
| `OLLAMA_SERVER_URL` | *(none)* | Local or `https://ollama.com` |
| `OLLAMA_SERVER_API_KEY` | *(none)* | Required for Ollama Cloud |
| `OLLAMA_SERVER_MODEL` | *(none)* | Default model |
| `OLLAMA_SERVER_CONFIG_PATH` | *(none)* | Provider YAML path |
| `OLLAMA_SERVER_PULL_MODELS_TIMEOUT` | `600` | Pull timeout (seconds) |
| `OLLAMA_SERVER_PULL_MODELS_ENABLED` | `false` | Auto-pull models |
| `OLLAMA_SERVER_LOAD_MODELS_ENABLED` | `false` | List models from server API |

## Embeddings

| Variable | Type | Default | Description |
| --- | --- | --- | --- |
| `EMBEDDING_URL` | string | *(none)* | Override embedder base URL |
| `EMBEDDING_KEY` | string | *(none)* | Override embedder API key |
| `EMBEDDING_MODEL` | string | *(none)* | Embedding model id |
| `EMBEDDING_PROVIDER` | string | `openai` | `openai`, `ollama`, `mistral`, `jina`, `huggingface` |
| `EMBEDDING_STRIP_NEW_LINES` | bool | `true` | Strip newlines before embed |
| `EMBEDDING_BATCH_SIZE` | int | `512` | Batch size |
| `EMBEDDING_MAX_TEXT_BYTES` | int | `8192` | Max bytes per document sent to embedder (full text kept in DB) |

When `EMBEDDING_KEY` / `EMBEDDING_URL` are empty, OpenAI embedder falls back to `OPEN_AI_KEY` / `OPEN_AI_SERVER_URL`.

## Chain summarizer

### Flow summarizer

| Variable | Default |
| --- | --- |
| `SUMMARIZER_PRESERVE_LAST` | `true` |
| `SUMMARIZER_USE_QA` | `true` |
| `SUMMARIZER_SUM_MSG_HUMAN_IN_QA` | `false` |
| `SUMMARIZER_LAST_SEC_BYTES` | `51200` |
| `SUMMARIZER_MAX_BP_BYTES` | `16384` |
| `SUMMARIZER_MAX_QA_SECTIONS` | `10` |
| `SUMMARIZER_MAX_QA_BYTES` | `65536` |
| `SUMMARIZER_KEEP_QA_SECTIONS` | `1` |

### Assistant mode

| Variable | Default |
| --- | --- |
| `ASSISTANT_USE_AGENTS` | `false` |
| `ASSISTANT_SUMMARIZER_PRESERVE_LAST` | `true` |
| `ASSISTANT_SUMMARIZER_LAST_SEC_BYTES` | `76800` |
| `ASSISTANT_SUMMARIZER_MAX_BP_BYTES` | `16384` |
| `ASSISTANT_SUMMARIZER_MAX_QA_SECTIONS` | `7` |
| `ASSISTANT_SUMMARIZER_MAX_QA_BYTES` | `76800` |
| `ASSISTANT_SUMMARIZER_KEEP_QA_SECTIONS` | `3` |

## Search engines

| Variable | Default | Description |
| --- | --- | --- |
| `DUCKDUCKGO_ENABLED` | `true` | Enable DuckDuckGo tool |
| `DUCKDUCKGO_REGION` | *(none)* | e.g. `us-en` |
| `DUCKDUCKGO_SAFESEARCH` | *(none)* | `off` / `moderate` / `strict` |
| `DUCKDUCKGO_TIME_RANGE` | *(none)* | `d` / `w` / `m` / `y` |
| `SPLOITUS_ENABLED` | `false` | Sploitus exploit search (IP reputation matters) |
| `GOOGLE_API_KEY` | *(none)* | Google CSE API key |
| `GOOGLE_CX_KEY` | *(none)* | Custom Search Engine id |
| `GOOGLE_LR_KEY` | `lang_en` | Language restriction |
| `TRAVERSAAL_API_KEY` | *(none)* | Traversaal |
| `TAVILY_API_KEY` | *(none)* | Tavily |
| `PERPLEXITY_API_KEY` | *(none)* | Perplexity |
| `PERPLEXITY_MODEL` | `sonar` | Perplexity model |
| `PERPLEXITY_CONTEXT_SIZE` | `low` | `low` / `medium` / `high` |
| `SEARXNG_URL` | *(none)* | Searxng base URL |
| `SEARXNG_CATEGORIES` | `general` | Categories |
| `SEARXNG_LANGUAGE` | *(none)* | Language filter |
| `SEARXNG_SAFESEARCH` | `0` | `0` / `1` / `2` |
| `SEARXNG_TIME_RANGE` | *(none)* | Time filter |
| `SEARXNG_TIMEOUT` | *(none)* | Request timeout seconds |

Search tools also use `PROXY_URL` when set.

## Proxy, TLS outbound, and HTTP timeouts

| Variable | Type | Default | Description |
| --- | --- | --- | --- |
| `PROXY_URL` | string | *(none)* | HTTP proxy for providers and network tools |
| `EXTERNAL_SSL_CA_PATH` | string | `""` | Extra CA for outbound LLM TLS |
| `EXTERNAL_SSL_INSECURE` | bool | `false` | Skip TLS verify (dev only) |
| `HTTP_CLIENT_TIMEOUT` | int | `600` | Outbound HTTP timeout seconds; `0` = no timeout (not recommended) |

## Agent supervision and limits

| Variable | Default | Description |
| --- | --- | --- |
| `EXECUTION_MONITOR_ENABLED` | `false` | Mentor review on tool-loop patterns |
| `EXECUTION_MONITOR_SAME_TOOL_LIMIT` | `5` | Consecutive identical tool calls before mentor |
| `EXECUTION_MONITOR_TOTAL_TOOL_LIMIT` | `10` | Total tool calls before mentor |
| `MAX_GENERAL_AGENT_TOOL_CALLS` | `100` | Cap for Assistant, Primary, Pentester, Coder, Installer |
| `MAX_LIMITED_AGENT_TOOL_CALLS` | `20` | Cap for Searcher, Memorist, Reporter, Adviser, Reflector, etc. |
| `AGENT_PLANNING_STEP_ENABLED` | `false` | Planner step before specialist agents |

## Graphiti knowledge graph

On `Config`:

| Variable | Default | Description |
| --- | --- | --- |
| `GRAPHITI_ENABLED` | `false` | Enable graph integration |
| `GRAPHITI_TIMEOUT` | `30` | Client timeout seconds |
| `GRAPHITI_URL` | *(none)* | Graphiti API base (compose: `http://graphiti:8000`) |

Compose/stack only (Graphiti service, not `Config`): `GRAPHITI_MODEL_NAME` (compose default `gpt-5-mini`), `NEO4J_USER`, `NEO4J_PASSWORD`, `NEO4J_DATABASE`, `NEO4J_URI`. Use `docker-compose-graphiti.yml` overlay.

## Observability and Langfuse

On `Config`:

| Variable | Default | Description |
| --- | --- | --- |
| `OTEL_HOST` | *(none)* | OpenTelemetry collector host/endpoint |
| `LANGFUSE_BASE_URL` | *(none)* | Langfuse API base |
| `LANGFUSE_PROJECT_ID` | *(none)* | Project id |
| `LANGFUSE_PUBLIC_KEY` | *(none)* | Public key |
| `LANGFUSE_SECRET_KEY` | *(none)* | Secret key |

`.env.example` also defines a large Langfuse/observability compose surface (`LANGFUSE_*` Postgres/ClickHouse/S3/Redis/init, Grafana/OTEL listen ports). Those configure optional stacks, not fields on `Config`. Wire `LANGFUSE_*` / `OTEL_HOST` on the `pentagi` service when overlays are enabled.

## Compose-only keys (quick map)

| Prefix / key | Consumed by | Purpose |
| --- | --- | --- |
| `PENTAGI_LISTEN_*`, `PENTAGI_IMAGE`, volume dirs | `docker-compose.yml` | Host publish ports, image, mounts |
| `PENTAGI_POSTGRES_*` | compose → `DATABASE_URL` | DB credentials for pgvector service |
| `PENTAGI_LLM_SERVER_CONFIG_PATH`, `PENTAGI_OLLAMA_SERVER_CONFIG_PATH` | volume mounts | Host YAML → in-container config paths |
| `LOCAL_SCRAPER_*`, `SCRAPER_LISTEN_*` | scraper service | Scraper auth and ports |
| `PGVECTOR_LISTEN_*`, `POSTGRES_EXPORTER_LISTEN_*` | compose | Optional host port publish |
| `LANGFUSE_*` (stack), `NEO4J_*`, Grafana/OTEL listen | optional compose files | Side stacks |

## Secret handling

`Config.GetSecretPatterns()` builds redaction regexes for non-empty secret fields (API keys, OAuth secrets, DB URL, proxy URL with credentials, Langfuse keys, cookie salt, license key, and related). Values are trimmed; empty/whitespace entries are skipped. Use this when logging tool output or chain content that may echo credentials.

## Verification and common failures

| Symptom | Check |
| --- | --- |
| No providers in UI / flow fails at LLM | At least one key + reachable server URL; for custom/Ollama, config path mounts |
| DB connection refused | `DATABASE_URL` host (`pgvector` in compose vs localhost for bare metal); pool limits |
| OAuth redirect mismatch | Exact `PUBLIC_URL` + `/api/v1/auth/login-callback` |
| CORS browser errors | `CORS_ORIGINS` matches browser origin (not only `*`) |
| Terminal / docker errors | `DOCKER_SOCKET` / `DOCKER_HOST`, `DOCKER_INSIDE`, socket mount |
| Search tools missing | Keys/enable flags; `PROXY_URL` if network requires proxy |
| Graphiti tool no-ops | `GRAPHITI_ENABLED=true`, `GRAPHITI_URL`, Neo4j stack healthy |
| TLS to private LLM fails | `EXTERNAL_SSL_CA_PATH` or controlled `EXTERNAL_SSL_INSECURE` |
| Long LLM calls abort | Raise `HTTP_CLIENT_TIMEOUT` (default 600s) |

```bash
# From repo root after editing .env
docker compose config >/dev/null   # validate interpolation
docker compose up -d
# Confirm pentagi sees expected env (example)
docker compose exec pentagi env | rg 'OPEN_AI_|DATABASE_|SERVER_|GRAPHITI_|LANGFUSE_|OTEL_'
```

## Related pages

<CardGroup>
  <Card title="Installation" href="/installation">
    Compose core stack, `.env` from `.env.example`, SSL volumes, optional overlays.
  </Card>
  <Card title="Configure LLM providers" href="/configure-llm-providers">
    Wire cloud and regional providers; test before first flow.
  </Card>
  <Card title="Local and custom providers" href="/local-and-custom-providers">
    `LLM_SERVER_*`, Ollama, YAML config paths, aggregators.
  </Card>
  <Card title="Search engines" href="/search-engines">
    Enable DuckDuckGo, Google, Tavily, Perplexity, Searxng, Sploitus.
  </Card>
  <Card title="Observability and Langfuse" href="/observability-and-langfuse">
    OTEL and Langfuse stacks and what each measures.
  </Card>
  <Card title="Knowledge graph" href="/knowledge-graph">
    Graphiti + Neo4j enablement and failure modes.
  </Card>
  <Card title="Docker sandbox and workers" href="/docker-sandbox-workers">
    Socket, `DOCKER_INSIDE`, images, multi-host constraints.
  </Card>
  <Card title="Agents and supervision" href="/agents-and-supervision">
    Monitor thresholds, tool-call hard limits, planning step.
  </Card>
</CardGroup>
