# How to Set Up PostgreSQL Persistence for OpenMAIC Docker Deployment

> Set up PostgreSQL persistence for your OpenMAIC Docker deployment. Learn to run a database container and enable server persistence for robust data storage.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: how-to-guide
- Published: 2026-09-08

---

**OpenMAIC uses browser-based storage by default, but you can switch to PostgreSQL persistence by running a database container alongside the app and enabling the `server-persistence` Docker Compose profile.**

The OpenMAIC platform from THU-MAIC stores classroom state and session data in IndexedDB when running locally. For production deployments or multi-instance setups, you need **server-side PostgreSQL persistence**. This guide walks through the exact configuration based on the official OpenMAIC source code.

## Prerequisites

Before starting, ensure you have Docker and Docker Compose installed. The OpenMAIC repository includes all necessary service definitions at the root level.

## Step 1: Add the PostgreSQL Service

OpenMAIC's [`docker-compose.yml`](https://github.com/THU-MAIC/OpenMAIC/blob/main/docker-compose.yml) defines an optional `postgres` service that you activate with a Docker profile. This service runs the official `postgres:16` image with persistent storage via a named volume.

Key configuration details from [[`docker-compose.yml`](https://github.com/THU-MAIC/OpenMAIC/blob/main/docker-compose.yml)](https://github.com/THU-MAIC/OpenMAIC/blob/main/docker-compose.yml#L43-L65):

- Service name: `postgres` (resolved automatically on the internal Docker network)
- Volume: `openmaic-postgres` mounted to `/var/lib/postgresql/data`
- Profile: `server-persistence` (prevents accidental startup)

```yaml

# Excerpt from docker-compose.yml

services:
  openmaic:
    # ... app configuration

  postgres:
    image: postgres:16
    profiles:
      - server-persistence
    environment:
      - POSTGRES_DB=openmaic
      - POSTGRES_USER=openmaic
      - POSTGRES_PASSWORD=${PERSISTENCE_POSTGRES_PASSWORD:-openmaic-dev}
    volumes:
      - openmaic-postgres:/var/lib/postgresql/data

```

No separate "persistence server" is required—the persistence logic is built directly into the OpenMAIC service.

## Step 2: Configure Connection Credentials

Copy the environment template and set your database connection parameters. The `DATABASE_URL` must point to the `postgres` container hostname, which Docker Compose resolves automatically.

```bash
cp .env.example .env.local

```

Edit `.env.local` with these required values from [`.env.example`](https://github.com/THU-MAIC/OpenMAIC/blob/main/.env.example#L49-L55):

```bash
DATABASE_URL=postgres://openmaic:openmaic-dev@postgres:5432/openmaic
PERSISTENCE_DEV_TOKEN=openmaic-local-dev

```

**Critical:** The `PERSISTENCE_DEV_TOKEN` enables built-in development authentication. This token must match the value you pass at runtime.

## Step 3: Enable the Server-Persistence Profile

OpenMAIC reads two build-time environment variables to activate PostgreSQL storage. According to [`deployment.mdx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/docs/content/docs/deployment.mdx#L69-L88), set:

- `NEXT_PUBLIC_PERSISTENCE=1` — switches backend from IndexedDB to PostgreSQL
- `NEXT_PUBLIC_PERSISTENCE_TOKEN=<your-token>` — must match `PERSISTENCE_DEV_TOKEN`

These flags are **compiled into the application**, so they must be present during the Docker build.

## Step 4: Start the Containers

Launch the full stack with the `server-persistence` profile. This command builds the OpenMAIC image, starts both containers, and attaches volumes.

```bash
NEXT_PUBLIC_PERSISTENCE=1 \
NEXT_PUBLIC_PERSISTENCE_TOKEN=openmaic-local-dev \
docker compose --profile server-persistence up --build

```

Verify the database connection from within the app container:

```bash
docker exec -it openmaic sh -c "pg_isready -U openmaic -d openmaic"

```

## Step 5: Understand Data Persistence Behavior

PostgreSQL data survives container restarts through the `openmaic-postgres` Docker volume. Note these important persistence details from the source documentation:

- The `PERSISTENCE_POSTGRES_PASSWORD` variable **only affects initialization**—changing it later does not update the existing database user
- All classroom state, generated content, and session data writes to PostgreSQL
- Multiple OpenMAIC instances can connect to the same database for shared state

## Troubleshooting Common Issues

### Container fails to start with "profile not found"

Ensure you include `--profile server-persistence` in your Docker Compose command. The `postgres` service does not start without this flag.

### Database connection errors

Verify `DATABASE_URL` uses `postgres` as the hostname—not `localhost` or `127.0.0.1`. Docker's internal DNS resolves service names automatically.

### Persistence not activating

Confirm `NEXT_PUBLIC_PERSISTENCE=1` is set **before** the build. These are compiled constants, not runtime configuration.

## Summary

- OpenMAIC's **built-in persistence layer** eliminates the need for separate middleware—just connect to PostgreSQL
- Use **`docker compose --profile server-persistence`** to enable both the app and database services
- Set **`DATABASE_URL`** and **`PERSISTENCE_DEV_TOKEN`** in `.env.local` for connection credentials
- Pass **`NEXT_PUBLIC_PERSISTENCE=1`** and matching token at build time to activate the PostgreSQL backend
- Data persists in the **`openmaic-postgres`** Docker volume across container restarts

## Frequently Asked Questions

### Does OpenMAIC require a separate persistence server?

No. As implemented in THU-MAIC/OpenMAIC, the persistence logic is compiled directly into the main application service. You only need to provide a PostgreSQL database connection—the app handles all storage operations internally.

### Why does the PostgreSQL password not update when I change the environment variable?

According to the `deployment.mdx` documentation, `PERSISTENCE_POSTGRES_PASSWORD` is used only during PostgreSQL's first initialization. After the database files are created in the `openmaic-postgres` volume, you must modify credentials through PostgreSQL's native tools or reset the volume entirely.

### Can I use an external PostgreSQL instance instead of Docker Compose?

Yes. Replace the `DATABASE_URL` in `.env.local` with any valid PostgreSQL connection string. The `server-persistence` profile is specifically for local development; production deployments often point to managed database services.

### What happens if NEXT_PUBLIC_PERSISTENCE is not set?

Without `NEXT_PUBLIC_PERSISTENCE=1` at build time, OpenMAIC defaults to **IndexedDB browser storage**. Classroom state remains entirely client-side, and the `DATABASE_URL` configuration is ignored.