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

# Aegra

> Aegra open-source agent server compatible with the LangGraph Agent Server API, running your own LangGraph agents on bundled PostgreSQL with pgvector and Redis. Single by default, optional HA PostgreSQL and replicas. Deploys into an existing GVC; one database credentials secret must exist first.

## Overview

Aegra is an open-source agent server that implements the LangGraph Agent Server API: assistants, threads, background runs, streaming, the store and crons for the LangGraph agents in your image. This template runs your agent image on a bundled PostgreSQL with pgvector, which holds threads, runs, checkpoints and the store, and a bundled single-node Redis for the job queue and run event streams. It works with the LangGraph SDK and with frontends such as Agent Chat UI.

> [!NOTE]
>
> This template does not create a GVC. It deploys into an existing GVC that you already have, and that GVC should have exactly one location, because the bundled database and Redis run in one location.

### What Gets Created

| Resource | Name | Purpose | Optional |
| - | - | - | - |
| Workload | `RELEASE_NAME-aegra` | Aegra API and background workers (standard, 1 replica by default, up to 10) on port `2026` | — |
| Identity | `RELEASE_NAME-aegra-identity` | Workload identity for Aegra | — |
| Policy | `RELEASE_NAME-aegra-policy` | Grants the identity `reveal` on exactly the secrets you name | — |
| Workload | `RELEASE_NAME-postgres` | PostgreSQL with pgvector (stateful, 1 replica) | `postgres.enabled: true` (default) |
| Volume set | `RELEASE_NAME-pg-vs` | PostgreSQL data volume | `postgres.enabled: true` (default) |
| Identity | `RELEASE_NAME-pg-identity` | Workload identity for PostgreSQL | `postgres.enabled: true` (default) |
| Policy | `RELEASE_NAME-pg-policy` | Grants the PostgreSQL identity `reveal` on the database credentials secret | `postgres.enabled: true` (default) |
| Workload | `RELEASE_NAME-postgres-ha` | Patroni PostgreSQL (stateful, 3 replicas by default) | `postgresHA.enabled: true` |
| Workload | `RELEASE_NAME-etcd` | etcd cluster for Patroni | `postgresHA.enabled: true` |
| Workload | `RELEASE_NAME-postgres-ha-proxy` | HAProxy endpoint that always points at the PostgreSQL leader | `postgresHA.enabled: true` |
| Volume set | `RELEASE_NAME-postgres-ha-vs` | PostgreSQL data volume, one per replica | `postgresHA.enabled: true` |
| Volume set | `RELEASE_NAME-etcd-vs` | etcd data volume | `postgresHA.enabled: true` |
| Identity | `RELEASE_NAME-postgres-ha-identity` | Workload identity for Patroni PostgreSQL | `postgresHA.enabled: true` |
| Policy | `RELEASE_NAME-postgres-ha-policy` | Grants the Patroni identity `reveal` on its startup secrets and the database credentials secret | `postgresHA.enabled: true` |
| Identity | `RELEASE_NAME-etcd-identity` | Workload identity for etcd | `postgresHA.enabled: true` |
| Policy | `RELEASE_NAME-etcd-policy` | Grants the etcd identity `reveal` on its startup secret | `postgresHA.enabled: true` |
| Secret | `RELEASE_NAME-patroni-startup` | Patroni startup script | `postgresHA.enabled: true` |
| Secret | `RELEASE_NAME-etcd-startup` | etcd startup script | `postgresHA.enabled: true` |
| Secret | `RELEASE_NAME-postgres-proxy-startup` | HAProxy startup script | `postgresHA.enabled: true` |
| Secret | `RELEASE_NAME-wal-g-backup-script` | Backup script used when backups are enabled | `postgresHA.enabled: true` |
| Workload | `RELEASE_NAME-redis` | Redis job queue and run event streams (single node) | — |
| Identity | `RELEASE_NAME-redis-identity` | Workload identity for Redis | — |
| Policy | `RELEASE_NAME-redis-policy` | Grants the Redis identity access to its configuration secret | — |
| Secret | `RELEASE_NAME-redis-config` | Redis configuration | — |

The template creates no credentials. The API is reachable only from inside the GVC until you set `publicAccess.enabled: true`.

## Prerequisites

**1. Create the database credentials secret**

The bundled PostgreSQL initializes with this **dictionary** secret and Aegra connects with it. It holds exactly three keys: `username` and `database` (letters, digits and `_`) and `password` (any characters):

```bash theme={null}
cpln secret create-dictionary --name DB_CREDENTIALS_SECRET_NAME \
  --entry username=aegra \
  --entry password="$(openssl rand -hex 32)" \
  --entry database=aegra
```

Set `postgres.config.credentialsSecretName` to this name (or `postgresHA.config.credentialsSecretName` when you use the HA database). Secret names are org-wide, so give each release its own secret.

**2. Create the optional secrets**

Each is a **dictionary** secret created the same way. Agent secrets hold any keys your agent reads, such as `OPENAI_API_KEY`. Langfuse tracing needs the keys `publicKey` and `secretKey`. A Redis password needs the key `password`, URL-safe (letters, digits, `-`, `_`, `.`). See [Agent secrets](#agent-secrets), [Langfuse tracing](#langfuse-tracing) and [Redis](#redis).

**3. Choose your agent image**

The default image runs Aegra's example agents. To serve your own graphs, build an image as described in [Bring Your Own Agent](#bring-your-own-agent).

> [!WARNING]
>
> Every secret your release names must exist **before** you install. If one does not, the deployment waits silently: `cpln logs` returns nothing, and the only place the missing secret is named is `status.versions[].message` in `cpln workload get-deployments RELEASE_NAME-aegra --gvc GVC_NAME -o yaml`. Create the secret and the deployment recovers on its own after several minutes, or run `cpln workload force-redeployment RELEASE_NAME-aegra --gvc GVC_NAME` to skip the wait.

Optional database backups need a bucket and a Control Plane cloud account; see [Backing Up](#backing-up).

## Installation

The only required value is the name of the database credentials secret from [Prerequisites](#prerequisites):

```bash theme={null}
cpln helm install RELEASE_NAME oci://ghcr.io/controlplane-com/templates/aegra \
  --version 1.0.0 --gvc GVC_NAME \
  --set postgres.config.credentialsSecretName=DB_CREDENTIALS_SECRET_NAME
```

To install with the highly available database, add `--set postgres.enabled=false --set postgresHA.enabled=true --set postgresHA.config.credentialsSecretName=DB_CREDENTIALS_SECRET_NAME`.

To install with the CLI, UI, Terraform or Pulumi, follow the instructions for your preferred method:

> [card] [UI](https://docs.controlplane.com/template-catalog/install-manage/ui.md)
>
> Browse, install, and manage templates visually

> [card] [CLI](https://docs.controlplane.com/template-catalog/install-manage/cli.md)
>
> Manage templates from your terminal

> [card] [Terraform](https://docs.controlplane.com/template-catalog/install-manage/terraform.md)
>
> Declare templates in your Terraform configurations

> [card] [Pulumi](https://docs.controlplane.com/template-catalog/install-manage/pulumi.md)
>
> Declare templates in your Pulumi programs

## Configuration

### Aegra

```yaml theme={null}
aegra:
  image: upstreamyard/aegra:0.11.0   # your agent image (see Bring Your Own Agent)
  # set aegra.replicas: 2 and postgresHA.enabled: true for near-zero-downtime upgrades and failover
  replicas: 1                   # 1-10
  resources:
    minCpu: 250m
    maxCpu: 1000m
    minMemory: 512Mi
    maxMemory: 1Gi
  requestTimeoutSeconds: 600    # longest /runs/wait or /runs/stream request on the public URL before it is cut (1-3600)
  extraEnv: []                  # plain settings, e.g. [{name: LOG_LEVEL, value: DEBUG}]
```

### Agent secrets

```yaml theme={null}
agentSecrets:
  secretName: ""                # e.g. my-aegra-agent-secrets
  keys: []                      # each listed key becomes an env var, e.g. [OPENAI_API_KEY]
```

### Access

```yaml theme={null}
publicAccess:
  enabled: false                # true = public *.cpln.app URL. Aegra has NO auth unless your image adds a handler
internalAccess:
  type: same-gvc                # none | same-gvc | same-org | workload-list
  workloads: []                 # used only with workload-list
```

### Langfuse tracing

```yaml theme={null}
langfuse:
  secretName: ""                # dictionary with publicKey and secretKey; "" = tracing off
  baseUrl: http://my-langfuse-langfuse-web.my-gvc.cpln.local:3000   # or https://cloud.langfuse.com
```

### Database

```yaml theme={null}
postgres:                       # every knob of the postgres template
  enabled: true
  image: pgvector/pgvector:0.8.7-pg18
  config:
    credentialsSecretName: my-aegra-db-credentials   # REQUIRED, create before install
  resources:
    minCpu: 250m
    maxCpu: 1000m
    minMemory: 512Mi
    maxMemory: 1Gi
  volumeset:
    capacity: 10                # GiB (minimum 10)
  backup:                       # scheduled backups to object storage (postgres template)
    enabled: false
    image: ghcr.io/controlplane-com/backup-images/postgres-backup:18.1.0
    schedule: "0 2 * * *"       # daily at 2am UTC
    resources:
      cpu: 100m
      memory: 128Mi
    provider: aws               # aws, gcp or minio
    aws:
      bucket: my-aegra-backup-bucket
      region: us-east-1
      cloudAccountName: my-aegra-backup-cloudaccount
      policyName: my-aegra-backup-policy
      prefix: postgres/backups
    gcp:
      bucket: my-aegra-backup-bucket
      cloudAccountName: my-aegra-backup-cloudaccount
      prefix: postgres/backups
    minio:                      # any S3-compatible endpoint
      endpoint: http://my-minio-workload:9000
      bucket: my-aegra-backup-bucket
      credentialsSecretName: my-aegra-minio-credentials   # dictionary: accessKey, secretKey
      prefix: postgres/backups

postgresHA:                     # every knob of the postgres-highly-available template
  enabled: false                # enable exactly one of postgres / postgresHA
  image: controlplanecorporation/patroni-postgres:0.8
  config:
    credentialsSecretName: my-aegra-db-credentials
  replicas: 3
  resources:                    # per Patroni replica
    minCpu: 250m
    maxCpu: 1000m
    minMemory: 512Mi
    maxMemory: 2Gi
  volumeset:
    capacity: 10                # GiB per replica (minimum 10)
  backup:                       # provider, aws, gcp, minio: same keys and defaults as postgres.backup
    enabled: false
    mode: logical               # logical or wal-g
    resources:
      cpu: 100m
      memory: 512Mi             # 128Mi OOM-kills wal-g mid-backup
    logical:
      image: ghcr.io/controlplane-com/backup-images/postgres-backup:17.1.0
      schedule: "0 2 * * *"     # daily at 2am UTC
    walg:
      intervalSeconds: 21600    # every 6 hours
```

### Redis

```yaml theme={null}
redis:
  mode: dev                     # required: one node; Aegra cannot use Sentinel
  redis:
    image: redis:8
    resources:
      maxCpu: 200m
      maxMemory: 256Mi
      minCpu: 80m
      minMemory: 128Mi
    auth:
      fromSecret:
        enabled: false          # true = Redis requires the password in the secret below
        name: ""                # e.g. my-aegra-redis
        passwordKey: password
```

### Bring Your Own Agent

The default image is a community build of Aegra 0.11.0 with Aegra's example agents: the graph `stress_test` runs with no model key, and `agent` needs `OPENAI_API_KEY` in the agent secrets. To serve your own graphs, build an image from it with your code and an `aegra.json`:

```dockerfile theme={null}
FROM upstreamyard/aegra:0.11.0
USER root
COPY requirements.txt /app/agents/requirements.txt
RUN pip install --no-cache-dir -r /app/agents/requirements.txt
COPY . /app/agents
USER 10001
ENV AEGRA_CONFIG=/app/agents/aegra.json
```

```json theme={null}
{
  "dependencies": ["."],
  "graphs": { "my_agent": "./my_agent/graph.py:graph" }
}
```

Build and push the image to your org's private registry, then set `aegra.image` to the pushed image, for example `ORG_NAME.registry.cpln.io/my-agent:1`:

```bash theme={null}
cpln image build --name my-agent:1 --dockerfile Dockerfile --push
```

Only your graphs are served; the example agents of the default image are not registered. Each graph is listed in `/assistants/search` with a generated id, and its `graph_id` is the name you listed in `aegra.json`. Run it by passing that name as `"assistant_id": "my_agent"`.

## Connecting

| What | Address | Notes |
| - | - | - |
| Internal API | `http://RELEASE_NAME-aegra.GVC_NAME.cpln.local:2026` | Reachable per `internalAccess` |
| From your machine | `cpln port-forward RELEASE_NAME-aegra 2026:2026 --gvc GVC_NAME`, then `http://localhost:2026` | Works with the public URL off |
| Public URL | `status.canonicalEndpoint` of `cpln workload get RELEASE_NAME-aegra --gvc GVC_NAME -o yaml` | Only with `publicAccess.enabled: true`; has no authentication unless your image adds a handler |
| API reference | `/docs` on any of the addresses above | Interactive OpenAPI page |

Point the LangGraph SDK (`langgraph_sdk.get_client(url=...)`) at the URL and use the graph name as the assistant id.

To check the server, forward the port and read its version:

```bash theme={null}
cpln port-forward RELEASE_NAME-aegra 2026:2026 --gvc GVC_NAME
curl http://localhost:2026/info
```

### Chat With an Agent in a Browser

Aegra has no UI of its own. To chat with an agent, open the hosted [Agent Chat UI](https://agentchat.vercel.app) with your public URL and a graph name:

```text theme={null}
https://agentchat.vercel.app/?apiUrl=PUBLIC_URL&assistantId=stress_test
```

The browser calls Aegra directly, so this needs `publicAccess.enabled: true`. Anyone with the public URL can call your agents unless your image adds an auth handler.

## Operations

### Backing Up

All state lives in PostgreSQL, so backing up the database backs up every thread, run, checkpoint and store entry. Enable `postgres.backup.enabled` (or `postgresHA.backup.enabled` with the HA database) and fill in the provider block. The bucket, cloud account and IAM policy are set up as described in the [postgres](https://docs.controlplane.com/template-catalog/templates/postgres.md) or [postgres-highly-available](https://docs.controlplane.com/template-catalog/templates/postgres-highly-available.md) template, and a MinIO provider needs a dictionary secret with the keys `accessKey` and `secretKey`. Restore steps are in those templates' pages.

### Scaling and Availability

Aegra replicas share PostgreSQL and Redis, so they are interchangeable. Set `aegra.replicas` up to 10 to spread background runs across them. A run on a replica that stops or hangs is picked up again by a live replica and retried from its last checkpoint; Aegra does not drain runs on shutdown.

For near-zero-downtime upgrades and failover, set `aegra.replicas: 2` and enable `postgresHA`. Redis stays a single node, because Aegra takes one Redis URL and cannot follow Sentinel; runs are recovered from PostgreSQL if Redis restarts.

### Troubleshooting

> [accordion] The deployment is stuck and cpln logs returns nothing
>
> A secret named in the values does not exist. Read `status.versions[].message` with `cpln workload get-deployments RELEASE_NAME-aegra --gvc GVC_NAME -o yaml`, create the secret, then run `cpln workload force-redeployment RELEASE_NAME-aegra --gvc GVC_NAME`.

> [accordion] Aegra restarts a few times on first install
>
> Aegra exits when PostgreSQL or Redis is not up yet and the platform restarts it. It settles once both are ready; no action is needed.

> [accordion] Streams or /runs/wait calls are cut off on the public URL
>
> The public endpoint cuts requests after `aegra.requestTimeoutSeconds` (default 600, maximum 3600). Raise it for longer calls. Calls from inside the GVC over `cpln.local` are not subject to it.

> [accordion] A run ends in error with couldn't get a connection after 30.00 sec
>
> The database was unreachable for more than 30 seconds, for example during a PostgreSQL failover. Aegra keeps restarting until the database answers, and the API returns 500 or 503 meanwhile. Submit the run again.

> [accordion] Assistant 'stress\_test' not found
>
> Your own image serves only your graphs; the example agents of the default image are not registered. Call your graph by the name listed in `aegra.json`.

> [accordion] A secret was rotated but Aegra still uses the old value
>
> Rotating a secret does not restart Aegra. Run `cpln workload force-redeployment RELEASE_NAME-aegra --gvc GVC_NAME`.

> [accordion] The chart refuses to render
>
> The template rejects `redis.mode` other than `dev`, `extraEnv` or `agentSecrets.keys` names that it sets itself (such as `DATABASE_URL`, `REDIS_URL`, `RUN_MIGRATIONS_ON_STARTUP`, `LANGFUSE_*` and `CPLN_*`), duplicate or invalid env names, `agentSecrets.keys` without `agentSecrets.secretName`, `aegra.replicas` above 10, `aegra.requestTimeoutSeconds` above 3600, and enabling both or neither database. The error names the offending value.

## Important Notes

* Aegra has no authentication unless your image configures an auth handler in `aegra.json`. Keep `publicAccess` off without one: anyone with the URL could run your agents and read every thread.
* Langfuse in another GVC: set that Langfuse release's `internalAccess` to `same-org`, or Aegra cannot reach it.
* After rotating any secret the release uses, run `cpln workload force-redeployment RELEASE_NAME-aegra --gvc GVC_NAME`.
* Firewall changes (`publicAccess`, `internalAccess`) take from 30 seconds to several minutes to take effect.
* The first `helm upgrade` after an install can restart the bundled database and Redis, which briefly interrupts the API.
* Use a single-location GVC: the bundled database and Redis run in one location.
* Uninstalling deletes the database volume, and with it every thread, run and checkpoint.

## External References

> [card] [Aegra documentation](https://docs.aegra.dev)
>
> Concepts, configuration and API

> [card] [Aegra deployment guide](https://docs.aegra.dev/guides/deployment)
>
> Deploying Aegra

> [card] [Aegra authentication](https://docs.aegra.dev/guides/authentication)
>
> Adding an auth handler

> [card] [Aegra on GitHub](https://github.com/aegra/aegra)
>
> Source and issues

> [card] [Agent Chat UI](https://github.com/langchain-ai/agent-chat-ui)
>
> Browser chat frontend for LangGraph servers
