# Installation

> Prerequisites, Docker Compose core stack, .env from .env.example, SSL and data volumes, and compose overlays for observability, Langfuse, and Graphiti.

- 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

- `README.md`
- `.env.example`
- `docker-compose.yml`
- `docker-compose-observability.yml`
- `docker-compose-langfuse.yml`
- `docker-compose-graphiti.yml`
- `Dockerfile`

---

---
title: "Installation"
description: "Prerequisites, Docker Compose core stack, .env from .env.example, SSL and data volumes, and compose overlays for observability, Langfuse, and Graphiti."
---

PentAGI deploys as a Docker Compose stack: the `vxcontrol/pentagi` application container, `vxcontrol/pgvector` (PostgreSQL + pgvector), optional `postgres-exporter`, and `vxcontrol/scraper`. Configuration is driven by a host `.env` file cloned from `.env.example`. Optional stacks layer on with additional compose files for OpenTelemetry/Grafana observability, Langfuse LLM analytics, and Graphiti/Neo4j knowledge graph.

## Prerequisites

| Requirement | Detail |
|---|---|
| Runtime | Docker Engine + Docker Compose v2, or Podman (rootless needs scraper port changes) |
| CPU | Minimum 2 vCPU |
| Memory | Minimum 4 GB RAM |
| Disk | Minimum 20 GB free |
| Network | Pull images (`vxcontrol/pentagi`, `vxcontrol/pgvector`, `vxcontrol/scraper`, and optional overlays) |
| LLM | At least one provider key or local endpoint (OpenAI, Anthropic, Gemini, Bedrock, Ollama, DeepSeek, GLM, Kimi, Qwen, or custom `LLM_SERVER_*`) |
| Docker access | Host Docker socket (default `/var/run/docker.sock`) so the app can spawn sandbox worker containers |

<Warning>
The core `pentagi` service runs as `root:root` and mounts the Docker socket so it can create isolated tool containers. Treat host Docker access as root-equivalent. For stronger isolation, use a separate worker node (see [Docker sandbox and worker nodes](/docker-sandbox-workers)).
</Warning>

## Deployment architecture

```text
Host
├── .env                          # secrets + wiring (from .env.example)
├── example.custom.provider.yml   # mounted → /opt/pentagi/conf/custom.provider.yml
├── example.ollama.provider.yml   # mounted → /opt/pentagi/conf/ollama.provider.yml
├── docker-compose.yml            # core: pentagi, pgvector, pgexporter, scraper
├── docker-compose-observability.yml   # optional overlay (needs core networks first)
├── docker-compose-langfuse.yml        # optional overlay
└── docker-compose-graphiti.yml        # optional overlay

Compose networks (created by core file):
  pentagi-network | observability-network | langfuse-network

Named volumes (core):
  pentagi-data → /opt/pentagi/data
  pentagi-ssl  → /opt/pentagi/ssl
  pentagi-ollama → /root/.ollama
  scraper-ssl
  pentagi-postgres-data
```

| Service | Image (default) | Host bind (default) | Role |
|---|---|---|---|
| `pentagi` | `vxcontrol/pentagi:latest` (`PENTAGI_IMAGE`) | `127.0.0.1:8443` → `8443` | API, UI, agents, Docker sandbox control |
| `pgvector` | `vxcontrol/pgvector:latest` | `127.0.0.1:5432` → `5432` | App DB + vector memory |
| `pgexporter` | `quay.io/prometheuscommunity/postgres-exporter:v0.16.0` | `127.0.0.1:9187` → `9187` | Postgres metrics |
| `scraper` | `vxcontrol/scraper:latest` | `127.0.0.1:9443` → `443` | Isolated browser/scrape sessions |

`pentagi` joins all three networks so optional overlays can reach it without republishing core ports.

## Install paths

### Interactive installer (recommended)

Prebuilt installer binaries (Linux/Windows/macOS, amd64/arm64) walk through system checks, `.env` generation, LLM and search setup, SSL hardening, and compose deploy. Docker socket access requires either `sudo ./installer` or membership in the `docker` group.

Installer downloads and full wizard behavior: [Interactive installer](/installer).

### Manual Compose install

<Steps>
<Step title="Create a working directory">
```bash
mkdir pentagi && cd pentagi
```
Clone the repo, or download only the compose and env artifacts you need.
</Step>

<Step title="Create .env from .env.example">
```bash
curl -o .env https://raw.githubusercontent.com/vxcontrol/pentagi/master/.env.example
```
Or, in a full checkout: `cp .env.example .env`.
</Step>

<Step title="Provide provider YAML stubs for compose mounts">
Compose always mounts host files for custom and Ollama agent configs. Create them before `up`:

```bash
curl -o example.custom.provider.yml \
  https://raw.githubusercontent.com/vxcontrol/pentagi/master/examples/configs/custom-openai.provider.yml
curl -o example.ollama.provider.yml \
  https://raw.githubusercontent.com/vxcontrol/pentagi/master/examples/configs/ollama-llama318b.provider.yml
```

Override host paths with `PENTAGI_LLM_SERVER_CONFIG_PATH` and `PENTAGI_OLLAMA_SERVER_CONFIG_PATH` if needed.
</Step>

<Step title="Set at least one LLM provider">
Minimum examples (edit `.env`):

```bash
# Cloud — pick one or more
OPEN_AI_KEY=...
ANTHROPIC_API_KEY=...
GEMINI_API_KEY=...

# Local Ollama
OLLAMA_SERVER_URL=http://host.docker.internal:11434
# or a reachable LAN URL from the container network

# Custom OpenAI-compatible (vLLM, OpenRouter, etc.)
LLM_SERVER_URL=http://...
LLM_SERVER_KEY=...
LLM_SERVER_MODEL=...
# LLM_SERVER_CONFIG_PATH is set inside the container by compose when the host file is mounted
```

Search engines (`DUCKDUCKGO_ENABLED`, `TAVILY_API_KEY`, `GOOGLE_*`, `SEARXNG_URL`, etc.) are optional but recommended for richer recon. Full key list: [Environment variables](/environment-variables).
</Step>

<Step title="Harden security defaults">
Change at least:

| Variable | Default intent | Action |
|---|---|---|
| `COOKIE_SIGNING_SALT` | Cookie signing salt | Set a long random string |
| `PENTAGI_POSTGRES_PASSWORD` | `postgres` | Strong password; keep `DATABASE_URL` consistent via compose interpolation |
| `PUBLIC_URL` | `https://localhost:8443` | Real public origin for OAuth redirects and UI |
| `CORS_ORIGINS` | `https://localhost:8443` | Exact browser origins that will call the UI/API |

Optional: strip inline comments if a tool treats them as values:

```bash
perl -i -pe 's/\s+#.*$//' .env
```
</Step>

<Step title="Start the core stack">
```bash
curl -O https://raw.githubusercontent.com/vxcontrol/pentagi/master/docker-compose.yml
docker compose up -d
```

Verification:

```bash
docker compose ps
# pentagi healthy dependency: pgvector healthcheck (pg_isready)
curl -kI https://localhost:8443
```

Open `https://localhost:8443`. Default local admin:

- Email: `admin@pentagi.com`
- Password: `admin`

Change the password on first login. There is no public self-service sign-up from the login page.
</Step>
</Steps>

<Check>
Core install is successful when `pgvector` is healthy, `pentagi` is running, HTTPS responds on `8443`, and the login page accepts the admin account.
</Check>

## Core stack details

### Environment file

Compose interpolates host `.env` into service `environment` and port/volume overrides. Important groups:

| Area | Keys (selection) |
|---|---|
| Listen binds | `PENTAGI_LISTEN_IP` (default `127.0.0.1`), `PENTAGI_LISTEN_PORT` (`8443`), scraper/Postgres exporter bind vars |
| Data paths | `PENTAGI_DATA_DIR`, `PENTAGI_SSL_DIR`, `PENTAGI_OLLAMA_DIR`, `PENTAGI_DOCKER_SOCKET` |
| App server | `SERVER_HOST` (`0.0.0.0`), `SERVER_PORT` (`8443`), `SERVER_USE_SSL` (`true`), `SERVER_SSL_CRT` / `SERVER_SSL_KEY` |
| Database | `PENTAGI_POSTGRES_USER`, `PENTAGI_POSTGRES_PASSWORD`, `PENTAGI_POSTGRES_DB` (`pentagidb`); compose builds `DATABASE_URL` to `pgvector:5432` |
| Docker sandbox | `DOCKER_HOST`, `DOCKER_INSIDE`, `DOCKER_NET_ADMIN`, `DOCKER_SOCKET`, `DOCKER_DEFAULT_IMAGE`, `DOCKER_DEFAULT_IMAGE_FOR_PENTEST` |
| Scraper | `SCRAPER_PRIVATE_URL` (default `https://someuser:somepass@scraper/`), `LOCAL_SCRAPER_USERNAME` / `PASSWORD` |
| Integrations | `OTEL_HOST`, `LANGFUSE_*`, `GRAPHITI_*` |

Inside the container, the app image entrypoint is `/opt/pentagi/bin/entrypoint.sh` → `/opt/pentagi/bin/pentagi` (multi-stage `Dockerfile`: Node frontend build + Go API build → Alpine runtime).

### SSL certificates

With `SERVER_USE_SSL=true` (compose default):

1. Entrypoint defaults paths to `ssl/server.key` and `ssl/server.crt` under the working directory (`/opt/pentagi`), which is volume-backed by `pentagi-ssl` → `/opt/pentagi/ssl`.
2. If both files exist, they are reused.
3. If missing, entrypoint generates a local CA and a `localhost` server cert (RSA 4096, multi-year validity), appends the CA to the cert chain, and removes temporary CA private key material after signing.
4. To supply your own certs, place them on the SSL volume (or host dir bound via `PENTAGI_SSL_DIR`) and set `SERVER_SSL_CRT` / `SERVER_SSL_KEY` to the in-container paths.

Browsers will warn on the auto-generated certificate. External CA bundles for outbound LLM/tool TLS use `EXTERNAL_SSL_CA_PATH` / `EXTERNAL_SSL_INSECURE` (paths inside the container, typically under `/opt/pentagi/ssl/`).

### Data volumes

| Volume / bind | Container path | Persistence |
|---|---|---|
| `pentagi-data` or `PENTAGI_DATA_DIR` | `/opt/pentagi/data` | App data |
| `pentagi-ssl` or `PENTAGI_SSL_DIR` | `/opt/pentagi/ssl` | TLS material |
| `pentagi-ollama` or `PENTAGI_OLLAMA_DIR` | `/root/.ollama` | Ollama keys/models path in-container |
| `pentagi-postgres-data` | `/var/lib/postgresql/data` | DB + embeddings store |
| `scraper-ssl` | `/usr/src/app/ssl` | Scraper TLS |
| Host Docker socket | `/var/run/docker.sock` | Runtime only (not a named volume) |
| Host provider YAMLs | `/opt/pentagi/conf/*.provider.yml` | Config mounts |

Destroying named volumes deletes DB and SSL state. Prefer `docker compose down` without `-v` for restarts.

### Image build (optional)

Production image is multi-stage:

1. `node:23-slim` — `pnpm install` + frontend production build into `/opt/pentagi/fe`
2. `golang:1.24-bookworm` — static `pentagi`, `ctester`, `ftester`, `etester` binaries
3. `alpine:3.23.3` — runtime user `pentagi` (group includes docker GID 998); compose still forces `user: root:root` for socket access

```bash
docker build -t local/pentagi:latest .
# then set PENTAGI_IMAGE=local/pentagi:latest in .env
```

## Optional compose overlays

Overlay files declare `external: true` for `pentagi-network`, `observability-network`, and/or `langfuse-network`. **Start core `docker-compose.yml` first** so those networks exist, then attach overlays.

### Observability (OpenTelemetry → Grafana)

```bash
# .env
OTEL_HOST=otelcol:8148
```

```bash
curl -O https://raw.githubusercontent.com/vxcontrol/pentagi/master/docker-compose-observability.yml
docker compose -f docker-compose.yml -f docker-compose-observability.yml up -d
```

| Component | Default host access | Purpose |
|---|---|---|
| Grafana | `127.0.0.1:3000` | Dashboards (`./observability/grafana/...`) |
| OTEL collector | gRPC `8148`, HTTP `4318` | Ingest from PentAGI (`OTEL_HOST=otelcol:8148`) |
| VictoriaMetrics, Loki, Jaeger, node-exporter, cAdvisor, ClickHouse | Internal to `observability-network` | Metrics, logs, traces |

Repo-local configs under `observability/` are bind-mounted into the stack.

### Langfuse (LLM analytics)

```bash
# .env — wire PentAGI → Langfuse
LANGFUSE_BASE_URL=http://langfuse-web:3000
LANGFUSE_PROJECT_ID=   # match LANGFUSE_INIT_PROJECT_ID
LANGFUSE_PUBLIC_KEY=   # match LANGFUSE_INIT_PROJECT_PUBLIC_KEY
LANGFUSE_SECRET_KEY=   # match LANGFUSE_INIT_PROJECT_SECRET_KEY
```

```bash
curl -O https://raw.githubusercontent.com/vxcontrol/pentagi/master/docker-compose-langfuse.yml
docker compose -f docker-compose.yml -f docker-compose-langfuse.yml up -d
```

Langfuse UI defaults to `127.0.0.1:4000` (`LANGFUSE_LISTEN_PORT`). Init admin and project keys live under `LANGFUSE_INIT_*` / `LANGFUSE_NEXTAUTH_*` in `.env.example`. Change salts, encryption key, Redis/Postgres/MinIO credentials before production use.

To also ship Langfuse telemetry into the observability stack:

```bash
LANGFUSE_OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4318
```

### Graphiti knowledge graph (beta)

```bash
# .env
GRAPHITI_ENABLED=true
GRAPHITI_TIMEOUT=30
GRAPHITI_URL=http://graphiti:8000
GRAPHITI_MODEL_NAME=gpt-5-mini
NEO4J_USER=neo4j
NEO4J_PASSWORD=devpassword   # change in production
NEO4J_URI=bolt://neo4j:7687
OPEN_AI_KEY=...              # required by Graphiti entity extraction
```

```bash
curl -O https://raw.githubusercontent.com/vxcontrol/pentagi/master/docker-compose-graphiti.yml
docker compose -f docker-compose.yml -f docker-compose-graphiti.yml up -d
```

| Service | Host ports | Notes |
|---|---|---|
| `neo4j` | `127.0.0.1:7474`, `7687` | Browser + Bolt; volume `neo4j_data` |
| `graphiti` | `127.0.0.1:8000` | Healthcheck `/healthcheck`; API docs `/docs` |

Graphiti uses **only** the OpenAI-compatible endpoint from `OPEN_AI_KEY` / `OPEN_AI_SERVER_URL` (mapped as `OPENAI_API_KEY` / `OPENAI_BASE_URL`). Anthropic, Gemini, Bedrock, and other PentAGI providers are not used for graph extraction. Leave `GRAPHITI_ENABLED=false` if that endpoint is unavailable.

### All overlays together

```bash
docker compose \
  -f docker-compose.yml \
  -f docker-compose-langfuse.yml \
  -f docker-compose-graphiti.yml \
  -f docker-compose-observability.yml \
  up -d
```

Optional shell aliases:

```bash
alias pentagi="docker compose -f docker-compose.yml -f docker-compose-langfuse.yml -f docker-compose-graphiti.yml -f docker-compose-observability.yml"
alias pentagi-up="pentagi up -d"
alias pentagi-down="pentagi down"
```

## Network exposure

Default binds are localhost-only. For LAN access:

```bash
PENTAGI_LISTEN_IP=0.0.0.0
PENTAGI_LISTEN_PORT=8443
PUBLIC_URL=https://192.168.1.100:8443
CORS_ORIGINS=https://localhost:8443,https://192.168.1.100:8443
```

Then:

```bash
docker compose down
docker compose up -d --force-recreate
docker ps | grep pentagi   # expect 0.0.0.0:8443->8443/tcp
```

Do not put `0.0.0.0` in `PUBLIC_URL` or `CORS_ORIGINS`. Open host firewall port `8443/tcp` as needed.

## Podman notes

| Mode | Scraper config |
|---|---|
| Rootful | Same as Docker (`SCRAPER_PRIVATE_URL=https://user:pass@scraper/`) |
| Rootless | Privileged port 443 is unavailable; use non-privileged HTTP, e.g. `SCRAPER_PRIVATE_URL=http://someuser:somepass@scraper:3000/` (see comments in `.env.example`) |

## Operations and troubleshooting

| Symptom | Likely cause | Fix |
|---|---|---|
| Missing `pentagi-network` / `observability-network` / `langfuse-network` | Overlay started without core | `docker compose -f docker-compose.yml up -d` first, then overlays |
| `pentagi` waits / restarts on DB | `pgvector` not healthy or wrong password | Check `PENTAGI_POSTGRES_*`; `docker compose logs pgvector` |
| Port still `127.0.0.1` after edit | Env not reloaded | `docker compose up -d --force-recreate` |
| No LLM / empty provider list | No keys or unreachable local URL | Set at least one provider; verify URLs from inside the container network |
| Docker sandbox failures | Socket not mounted or permission denied | Confirm `PENTAGI_DOCKER_SOCKET` / compose volume; root user in compose |
| TLS browser warning | Auto-generated cert | Expected; replace with real certs on `pentagi-ssl` |
| Graphiti unhealthy / no graph tool value | Missing `OPEN_AI_KEY` or stack not attached | Enable overlay + OpenAI-compatible key, or set `GRAPHITI_ENABLED=false` |
| IDE loads bad env values | Inline comments in `.env` | Strip comments with the `perl` one-liner above |

Useful commands:

```bash
docker compose logs -f pentagi
docker compose -f docker-compose.yml -f docker-compose-graphiti.yml ps graphiti neo4j
docker compose -f docker-compose.yml -f docker-compose-graphiti.yml logs -f graphiti
```

Swagger (after UI is up): `https://localhost:8443/api/v1/swagger/index.html`.

## What stays out of the web UI

After install, the console manages provider *profiles*, prompts, and API tokens. These remain server-side via `.env` / compose:

- Provider API keys and base URLs
- Search engine credentials
- Langfuse, OTEL, Graphiti wiring
- Docker sandbox and network policy (`DOCKER_*`)
- Listen addresses and SSL material

## Next

<CardGroup>
<Card title="Quickstart" href="/quickstart">
First login, change admin defaults, create a flow, and verify provider health.
</Card>
<Card title="Interactive installer" href="/installer">
TUI wizard: system checks, .env generation, SSL hardening, and admin password reset.
</Card>
<Card title="Environment variables" href="/environment-variables">
Full Config / .env.example reference with defaults.
</Card>
<Card title="Configure LLM providers" href="/configure-llm-providers">
Wire cloud and local providers after the stack is running.
</Card>
<Card title="Observability and Langfuse" href="/observability-and-langfuse">
What OTEL_HOST and LANGFUSE_* measure and how the optional stacks connect.
</Card>
<Card title="Knowledge graph" href="/knowledge-graph">
Graphiti enablement, GRAPHITI_* settings, and failure modes.
</Card>
<Card title="Docker sandbox and worker nodes" href="/docker-sandbox-workers">
Socket options, images, and multi-host worker isolation.
</Card>
</CardGroup>
