Database Integrations
SQLite, PostgreSQL, MySQL, and MS SQL Server — how to configure each, Alembic migrations, connection pooling, and ChromaDB for vector storage.
Overview
Luna uses SQLAlchemy as its ORM and supports four database backends: SQLite (default, zero-config), PostgreSQL, MySQL/MariaDB, and MS SQL Server. All use the same schema and the same migration path via Alembic.
ChromaDB stores embedding vectors for semantic fact retrieval and episodic memory. It runs as a separate embedded database alongside the main relational store.
| Database | Use case | Package |
|---|---|---|
| SQLite | Local / development / single-user | Built-in (no extra deps) |
| PostgreSQL | Production, Supabase, high concurrency | psycopg2-binary |
| MySQL / MariaDB | Existing MySQL infrastructure | pymysql |
| MS SQL Server | Enterprise / Azure SQL | pyodbc |
| ChromaDB | Vector embeddings (all backends) | chromadb (included) |
SQLite (default)
SQLite is the default and requires no configuration. The database file is created at data/luna.db on first run.
# Leave DB_URL blank — SQLite is used automatically
# DB_URL= ← not set
# Optional: change the file path
DB_PATH=data/luna.db
# Optional: custom data directory
LUNA_DATA_DIR=/path/to/luna-dataSQLite limitations
- No connection pool (
DB_POOL_SIZEis ignored). - Write concurrency is limited by SQLite's file-level locking.
- Not suitable for multi-process deployments (Gunicorn with workers > 1).
PostgreSQL / Supabase
DB_URL=postgresql+psycopg2://user:password@localhost:5432/luna
# Supabase (pooler URL — recommended for serverless):
DB_URL=postgresql+psycopg2://postgres.xxxx:password@aws-0-us-east-1.pooler.supabase.com:6543/postgrespip install psycopg2-binaryPostgreSQL-specific settings
DB_POOL_SIZE=10 # persistent connections in the pool
DB_MAX_OVERFLOW=20 # extra connections above pool_size under load
DB_POOL_TIMEOUT=30 # seconds to wait for a free connection
DB_POOL_RECYCLE=1800 # recycle connections every 30 min (prevents stale TCP)
DB_ECHO=false # log every SQL statement (development only)Supabase setup
- Create a project at
supabase.com. - Go to Project Settings → Database → Connection string → Python.
- Copy the connection string into
DB_URL. - Run
alembic upgrade headto create the schema.
MySQL / MariaDB
DB_URL=mysql+pymysql://user:password@localhost:3306/luna
# With charset (recommended):
DB_URL=mysql+pymysql://user:password@localhost:3306/luna?charset=utf8mb4pip install pymysqlCREATE DATABASE luna CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'luna_user'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON luna.* TO 'luna_user'@'localhost';
FLUSH PRIVILEGES;utf8mb4 charset to support emoji and full Unicode in conversation content and memory facts.MS SQL Server / Azure SQL
DB_URL=mssql+pyodbc://user:password@server/luna?driver=ODBC+Driver+17+for+SQL+ServerDB_URL=mssql+pyodbc://@myserver.database.windows.net/luna?driver=ODBC+Driver+17+for+SQL+Server&Authentication=ActiveDirectoryInteractivepip install pyodbc
# Also install the ODBC driver: https://learn.microsoft.com/sql/connect/odbc/download-odbc-driver-for-sql-serverCreate the database (T-SQL)
CREATE DATABASE luna COLLATE SQL_Latin1_General_CP1_CI_AS;
CREATE LOGIN luna_user WITH PASSWORD = 'YourPasswordHere';
USE luna;
CREATE USER luna_user FOR LOGIN luna_user;
ALTER ROLE db_owner ADD MEMBER luna_user;Connection pool
SQLAlchemy's connection pool is configured globally. These settings apply to PostgreSQL, MySQL, and MS SQL Server (not SQLite):
| Env var | Default | Description |
|---|---|---|
DB_POOL_SIZE | 10 | Persistent connections maintained at all times. |
DB_MAX_OVERFLOW | 20 | Extra connections allowed above pool_size during spikes. |
DB_POOL_TIMEOUT | 30 | Seconds to wait for a free connection before raising. |
DB_POOL_RECYCLE | 1800 | Max connection age in seconds (prevents stale TCP drops). |
DB_ECHO | false | Log every SQL statement to stdout (dev/debug only). |
The total max connections = DB_POOL_SIZE + DB_MAX_OVERFLOW. For Supabase or PlanetScale with connection limits, reduce DB_POOL_SIZEto 5 and DB_MAX_OVERFLOW to 10.
Migrations (Alembic)
Luna uses Alembic for schema management. Run migrations after switching database backends or updating Luna.
# Apply all pending migrations
alembic upgrade head
# Check current revision
alembic current
# Show migration history
alembic history
# Roll back one step
alembic downgrade -1alembic upgrade head after changing DB_URLto a new backend. The first run creates all tables from scratch.Creating a migration
# After editing SQLAlchemy models in backend/models/database.py:
alembic revision --autogenerate -m "add my_new_column"
alembic upgrade headSchema overview
Key tables in backend/models/database.py:
| Table | Description |
|---|---|
conversations | Conversation sessions with title and metadata. |
messages | Individual messages (role, content, timestamps). |
facts | Long/short-term memory facts with category, confidence, importance. |
fact_relationships | Typed graph edges between facts (CONTRADICTS, CONFIRMS, etc.). |
episodes | Conversation episode summaries with key fact IDs and embeddings. |
personality_state | Current mood and style preference floats. |
state_events | Historical state inferences for pattern learning. |
tasks | User tasks with due date, priority, and completion status. |
calendar_events | Scheduled events with start/end datetime. |
proactive_log | Record of proactive messages sent by the scheduler. |
audit_log | Tool call history with args, results, and conversation ID. |
contradiction_notes | Records of detected fact contradictions. |
ChromaDB (vectors)
ChromaDB stores embedding vectors for two purposes:
luna_facts— fact embeddings for semantic retrieval inMemoryManager.luna_episodes— episode embeddings for long-range conversation recall inMemoryGraph.
# ChromaDB storage path (relative to LUNA_DATA_DIR)
CHROMA_PATH=data/chroma # default
# Embedding provider (used by ChromaDB ingestion)
EMBEDDING_PROVIDER=ollama # or openai-compatibleChromaDB runs embedded in the same process — no separate server. Data is persisted to disk at CHROMA_PATH. Deleting this directory clears all vectors (facts and episodes remain in the relational DB but lose semantic search until re-embedded).
Backup and restore
SQLite
# Backup
cp data/luna.db data/backups/luna-$(date +%Y%m%d).db
# Restore
cp data/backups/luna-20250601.db data/luna.dbPostgreSQL
pg_dump luna > luna-backup.sql
psql luna < luna-backup.sqlChromaDB
# ChromaDB is a directory — copy the entire folder
cp -r data/chroma data/backups/chroma-$(date +%Y%m%d)Luna automatically creates daily backups of SQLite to data/backups/via the scheduler. For PostgreSQL and MySQL, configure your own backup strategy.