How to Set Up PostgreSQL Persistence for OpenMAIC: A Complete Docker Guide

To set up PostgreSQL persistence for OpenMAIC, copy .env.example to .env.local, configure the DATABASE_URL connection string pointing to the Docker PostgreSQL service, set the required build-time environment flags, and launch the server-persistence Docker Compose profile.

By default, OpenMAIC stores classroom state in the browser using IndexedDB. For production stability and multi-user support, you need to enable server-side persistence using PostgreSQL. This guide explains how to configure the server-persistence profile in the THU-MAIC/OpenMAIC repository to persist data across container restarts using PostgreSQL 16.

Prerequisites and Environment Configuration

OpenMAIC requires specific environment variables to connect the Next.js application to the PostgreSQL backend. These settings must be configured before building the Docker images.

Copy the Environment Template

Start by creating your local environment file from the provided template:

cp .env.example .env.local

The .env.example file contains placeholders for all required variables, including database connection details and authentication tokens.

Configure the Database Connection String

Edit .env.local and set DATABASE_URL to point at the PostgreSQL container that Docker Compose will start:

DATABASE_URL=postgres://openmaic:openmaic-dev@postgres:5432/openmaic

This connection string uses the following components:

  • openmaic – the database user created automatically on first container start
  • openmaic-dev – the default password (controlled by PERSISTENCE_POSTGRES_PASSWORD)
  • postgres – the Docker service name resolving to the database container
  • 5432 – the default PostgreSQL port
  • openmaic – the target database name

Set Persistence Tokens

Add a development token to .env.local for local authentication between the client and server:

PERSISTENCE_DEV_TOKEN=openmaic-local-dev

Important: The value must match the NEXT_PUBLIC_PERSISTENCE_TOKEN you set during the Docker build step.

Launching the Server Persistence Profile

Unlike runtime variables, NEXT_PUBLIC_PERSISTENCE and NEXT_PUBLIC_PERSISTENCE_TOKEN are build-time flags. You must export them before Docker builds the image to ensure the client bundle includes the correct API endpoints.

Run the following command to build and launch the stack:

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

This command starts three key components defined in docker-compose.yml:

  • openmaic – The Next.js application running on Node 22
  • postgres – PostgreSQL 16 with the node-postgres driver
  • openmaic-postgres – A named Docker volume that persists database files across container restarts

The openmaic-postgres volume retains all data. Note that PERSISTENCE_POSTGRES_PASSWORD only initializes the superuser password on the first volume creation; changing this variable later will not update existing credentials.

Verifying Database Persistence

Once the containers are running, confirm that data persists outside the browser.

Test Via the Application

  1. Navigate to http://localhost:3000 (default port).
  2. Create a classroom or upload a course document.
  3. Refresh the browser page or run docker compose restart.
  4. Verify the data remains loaded, confirming it is stored in PostgreSQL rather than IndexedDB.

Inspect Data Directly

Connect to the running PostgreSQL container using the psql client to inspect tables directly:

docker exec -it $(docker ps -q -f name=postgres) psql -U openmaic -d openmaic

You can query the runtime state tables to verify that classroom data is committed to disk.

Key Implementation Files

Understanding the source files helps debug connection issues or extend persistence functionality.

.env.example

This template in the repository root defines all required environment variables, including DATABASE_URL and PERSISTENCE_DEV_TOKEN, ensuring consistent configuration across development environments.

docker-compose.yml

The Compose configuration defines the server-persistence profile, service dependencies between the OpenMAIC app and PostgreSQL, and the named volume openmaic-postgres for data durability.

packages/@openmaic/storage/runtime-store.pg.ts

This TypeScript file implements the PostgreSQL-backed persistence layer used by the server-side API. It handles connection pooling, query execution, and serialization of classroom state to relational tables.

app/workspace/page.tsx

The frontend entry point checks NEXT_PUBLIC_PERSISTENCE at build time to determine whether to instantiate the IndexedDB client-side store or proxy requests through the server persistence API.

packages/docs/content/docs/deployment.mdx

The official documentation file contains human-readable deployment instructions and advanced configuration options for production PostgreSQL setups.

Summary

  • Copy .env.example to .env.local and set DATABASE_URL to postgres://openmaic:openmaic-dev@postgres:5432/openmaic.
  • Export NEXT_PUBLIC_PERSISTENCE=1 and matching NEXT_PUBLIC_PERSISTENCE_TOKEN as build-time flags before running Docker commands.
  • Launch the stack using docker compose --profile server-persistence up --build to start both the Node.js application and PostgreSQL 16.
  • Persist all classroom state and course data in the named Docker volume openmaic-postgres, ensuring survival across container restarts.

Frequently Asked Questions

Why does changing the PostgreSQL password environment variable not affect my existing data?

The PERSISTENCE_POSTGRES_PASSWORD variable only initializes the database superuser password when the openmaic-postgres volume is created for the first time. Because the volume persists across container restarts, subsequent changes to this environment variable do not alter the existing PostgreSQL user credentials or database files.

Can I connect OpenMAIC to an external PostgreSQL database instead of the Docker container?

Yes. Update the DATABASE_URL in .env.local to point to your external instance (e.g., postgres://user:pass@db.example.com:5432/openmaic). Ensure the database is network-accessible from the OpenMAIC container and that the schema is compatible with the queries implemented in packages/@openmaic/storage/runtime-store.pg.ts.

What is the difference between client-side and server-side persistence in OpenMAIC?

Client-side persistence stores classroom state in the browser's IndexedDB, as implemented in app/workspace/page.tsx. Server-side persistence activates when NEXT_PUBLIC_PERSISTENCE=1 is set at build time, causing the application to route all storage operations through HTTP API calls to the PostgreSQL backend defined in the DATABASE_URL.

Is it safe to use the development token in production environments?

No. The PERSISTENCE_DEV_TOKEN and NEXT_PUBLIC_PERSISTENCE_TOKEN are intended strictly for local development. Never use these default values in public deployments. For production, implement a proper authentication layer and avoid exposing NEXT_PUBLIC_PERSISTENCE unless your backend API includes robust authorization checks.

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 →