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-postgresmounted 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 PostgreSQLNEXT_PUBLIC_PERSISTENCE_TOKEN=<your-token>— must matchPERSISTENCE_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_PASSWORDvariable 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-persistenceto enable both the app and database services - Set
DATABASE_URLandPERSISTENCE_DEV_TOKENin.env.localfor connection credentials - Pass
NEXT_PUBLIC_PERSISTENCE=1and matching token at build time to activate the PostgreSQL backend - Data persists in the
openmaic-postgresDocker 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →