How to Set Up PostgreSQL Persistence for OpenMAIC Docker Deployment

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 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#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)

# 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.

cp .env.example .env.local

Edit .env.local with these required values from .env.example:

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, 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.

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:

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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →