Skip to main content
Relational databases store metadata, document information, and system state in Cognee. They track documents, chunks, and provenance (where data came from and how it’s linked).
New to configuration?See the Setup Configuration Overview for the complete workflow:install extras → create .env → choose providers → handle pruning.

Supported Providers

Cognee supports these relational database options:
  • SQLite — File-based database, works out of the box (default)
  • Postgres — Production-ready database for multi-process concurrency
  • Turso (libSQL) — A SQLite-compatible drop-in with optional embedded-replica sync for a hosted Turso database

Configuration

Set these environment variables in your .env file:
  • DB_PROVIDER — The database provider (sqlite, postgres, turso)
  • DB_NAME — Database name
  • DB_HOST — Database host (Postgres only)
  • DB_PORT — Database port (Postgres only)
  • DB_USERNAME — Database username (Postgres only)
  • DB_PASSWORD — Database password (Postgres only)
  • DB_TURSO_URL — Remote Turso database URL, e.g. libsql://<your-db>.turso.io (Turso remote mode only; leave unset for a local libSQL file)
  • DB_TURSO_AUTH_TOKEN — Auth token for the remote Turso database (Turso remote mode only)

Setup Guides

SQLite is file-based and requires no additional setup. It’s perfect for local development and single-user scenarios.
Installation: SQLite is included by default with Cognee. No additional installation required.Data Location: Data is stored under the Cognee system directory. You can override the root with SYSTEM_ROOT_DIRECTORY in your .env file.
Postgres is recommended for production environments, multi-process concurrency, or when you need external hosting.
Set the connection in your .env file:
Installation: Install the Postgres extras:
Docker Setup: Use the built-in Postgres service:
Docker Networking: When running Cognee in Docker and Postgres on your host, set:
Migrations: The Cognee API server runs startup migrations during its lifespan startup. For standalone scripts, CI, or deployments where you manage database lifecycle explicitly, run run_startup_migrations before serving traffic — especially the first time you point Cognee at a fresh external Postgres database, or after upgrading the cognee package:
Neon works with Cognee through the normal postgres relational provider. Cognee can use Neon Postgres for relational metadata, document information, chunks, pgvector storage, and Postgres graph state. Neon requires SSL/TLS for connections.Before configuring Cognee, create a Neon project, branch, database, and role, then copy the connection string from Connection Details in the Neon dashboard. A Neon connection string usually looks like this:
If your deployment uses DATABASE_URL, set it to the Neon connection string:
If you prefer split settings, map the same connection string into Cognee’s DB_* variables:
Install Postgres support in the environment where Cognee runs:
When using split settings, express Neon’s ?sslmode=require parameter as DATABASE_CONNECT_ARGS='{"ssl": "require"}'. DATABASE_CONNECT_ARGS must be valid JSON. Cognee forwards these arguments to the main relational engine, per-dataset PGVector engines, and the GRAPH_DATABASE_PROVIDER="postgres" graph engine.One Neon database can also back PGVector and the Postgres graph store:
For PGVector, enable the extension once in the Neon database:
Application database vs source database: DATABASE_URL or the DB_* variables configure Cognee’s own application database. Cognee uses this database for its internal metadata and state. To ingest an external Postgres database as data, pass that source database connection to cognee.add() instead:
That source database is separate from Cognee’s application database.Direct vs pooled Neon hosts: use the direct Neon host for setup, schema migrations, and default Cognee connections. The direct hostname does not contain -pooler:
Neon pooled hosts route through PgBouncer and contain -pooler in the hostname:
Prefer the direct host unless you have a specific need for pooled, high-concurrency application traffic after setup. Run migrations against the direct endpoint only. Neon PgBouncer does not support every session-level operation migrations and maintenance may rely on, and Cognee maintenance operations such as CREATE DATABASE and DROP DATABASE cannot run through the pooler. If setup or migrations fail on a -pooler host, switch to the direct host and retry.You can verify the same credentials with psql:
If you use Cognee’s relational database migration features with Neon, keep the application database and migration source database separate. Use direct hosts for both while running migrations:
Use a different MIGRATION_DB_NAME unless you intentionally want to migrate Cognee’s own internal tables into the knowledge graph.
A libSQL database file is a SQLite file, so Turso is a drop-in for the SQLite backend: Cognee talks to it through the same aiosqlite driver, the same sqlite dialect, and the same sqlite-dialect Alembic migrations. No migration changes are needed when switching between SQLite and Turso.Installation: Install the Turso extra:
Local / embedded: A libSQL file stored on disk under the Cognee data directory (named by DB_NAME). This is identical to the SQLite backend:
Remote (embedded replica): Set DB_PROVIDER="turso" and point at a hosted Turso database with DB_TURSO_URL and DB_TURSO_AUTH_TOKEN:
In remote mode Cognee reads and writes a fast local replica through aiosqlite exactly as in local mode, while libsql-experimental handles embedded-replica sync with the hosted primary. The replica is seeded from the primary before first use, and Cognee attempts a sync after each write within the operation. Seeding and syncing run off the event loop and are best-effort: a slow or unreachable primary is logged and never blocks or breaks a database operation, and the local replica stays usable.
The remote write path applies through aiosqlite; whether libSQL’s sync propagates those writes to the hosted primary depends on the driver’s replica write-capture and should be confirmed against a live Turso database. The local drop-in path is fully exercised offline.
Turso is a SQLite-compatible drop-in for Cognee’s core relational backend, but DLT-based ingestion connectors do not yet treat DB_PROVIDER="turso" the same as sqlite. The main add → cognify → search pipeline is covered; DLT connector support needs a follow-up.

Advanced Options

The MIGRATION_DB_* variables point to a source database that you want to extract and migrate into Cognee’s knowledge graph. This is entirely separate from the application database (DB_*) that Cognee uses for its own internal metadata and state.Does the migration DB need to be a different database than the application DB?In practice, use a different database (different DB_NAME / MIGRATION_DB_NAME) unless you intentionally want to migrate Cognee’s own internal tables into the knowledge graph. They can still live on the same Postgres server as long as they are different databases.
Use this when your source data is in a SQLite file, regardless of what DB_PROVIDER is set to:
See the Relational Database Migration example for a complete walkthrough of migrating schema and data into a knowledge graph.
Managed Postgres providers (Neon, RDS/Aurora, Azure Database for PostgreSQL) often require SSL. Pass asyncpg/SQLAlchemy connection arguments through the DATABASE_CONNECT_ARGS environment variable, which takes a JSON object:
These connect args are forwarded to Cognee’s main relational engine, per-dataset PGVector engines, and the GRAPH_DATABASE_PROVIDER="postgres" graph engine. The maintenance engine that runs CREATE/DROP DATABASE also uses the SSL setting. Leaving DATABASE_CONNECT_ARGS unset is a no-op, so in-cluster Postgres needs no change.The maintenance engine talks to Postgres over asyncpg, which expects an ssl key rather than libpq’s sslmode; if you supply sslmode, its value is mapped to asyncpg’s ssl for maintenance operations. For Neon specifically, the maintenance engine also rewrites a -pooler. host to its direct endpoint, because CREATE/DROP DATABASE cannot run through Neon’s PgBouncer connection pooler.The value must be a valid JSON object; invalid JSON raises a configuration error.
Enable per-user dataset isolation for multi-tenant scenarios.
This feature is available for both SQLite and Postgres.

Troubleshooting

Postgres Connectivity: Verify the database is listening on DB_HOST:DB_PORT and credentials are correct:
Docker Networking: Use host.docker.internal for host-to-container access on macOS/Windows.SQLite Concurrency: SQLite connections now open in WAL (Write-Ahead Logging) journal mode with synchronous=NORMAL and a 120-second busy timeout, and the driver connect timeout is also 120 seconds. This lets concurrent writers wait for the write lock (up to the busy timeout) instead of immediately failing, which greatly reduces the sqlite3.OperationalError: database is locked errors that could occur under Cognee’s parallel cognify() writes. No configuration is required — these settings apply automatically to every SQLite connection. WAL mode creates -wal and -shm sidecar files next to the database file; include them when backing up or copying the database. For heavy multi-user or multi-process workloads, still prefer Postgres.SQLite File Locks on Windows (pruning/deleting): When pruning or deleting a SQLite database, Cognee now disposes the cached SQLAlchemy engine (clearing the relational-engine cache and forcing garbage collection) before removing the file, so the underlying connection releases the file handle. If a stubborn Windows file lock still prevents removal after the retries, deletion no longer raises — it logs a warning and continues. In that case, the SQLite file may remain on disk and can be removed manually after the process releases the handle.
Neon requires SSL/TLS. If you use a full Neon connection string, keep sslmode=require in the URL:
If you use split DB_* settings instead of DATABASE_URL, pass SSL through DATABASE_CONNECT_ARGS:
DATABASE_CONNECT_ARGS must be valid JSON. Invalid JSON raises a configuration error before Cognee connects.
Operations that extract or answer over memory need an LLM provider. If remember(), cognify(), recall(), or related workflows fail because no LLM credentials are configured, set the provider API key in your environment:
See LLM Providers for provider-specific settings.
On Postgres and PGVector, Cognee connects through the asyncpg driver, which caches prepared statements per connection. When you place a transaction-mode connection pooler in front of Postgres — PgBouncer in transaction mode, or the Supabase / Neon connection poolers — a single client connection is multiplexed across many short-lived server backends. The cached statement names can then collide or vanish between checkouts, surfacing as:
or as intermittent connection is closed / InterfaceError pool errors under concurrency.Preferred fix — use the direct (session-mode) endpoint. Point DB_HOST (and VECTOR_DB_HOST) at the direct Postgres endpoint rather than the transaction pooler. Cognee already does this for its own CREATE/DROP DATABASE maintenance work, rewriting a Neon -pooler. host to the direct endpoint, because those statements cannot run through PgBouncer.If you must route through a transaction-mode pooler, disable asyncpg’s statement cache through DATABASE_CONNECT_ARGS:
These connect args are forwarded to the main relational engine, the per-dataset PGVector engines, and the GRAPH_DATABASE_PROVIDER="postgres" graph engine, so all three asyncpg connections stop caching prepared statements. You can combine them with the SSL keys in the same JSON object (for example {"ssl": "require", "statement_cache_size": 0}).
When the same Postgres backs relational metadata, PGVector, and the GRAPH_DATABASE_PROVIDER="postgres" graph store, Cognee can open more connections than a low connection limit allows — most commonly on Neon’s free tier — surfacing as:
Cognee opens a separate SQLAlchemy connection pool per engine, and the DB_* / DATABASE_CONNECT_ARGS settings are reused across all of them:
  • Relational engine — QueuePool with pool_size=5 and max_overflow=35 (up to 40 connections), plus pool_pre_ping=True and pool_recycle=280.
  • PGVector — when backend access control is off and the relational provider is Postgres, PGVector reuses the relational engine and adds no connections of its own. It creates its own pool only under ENABLE_BACKEND_ACCESS_CONTROL="true" (one engine per dataset, pool_size=2, max_overflow=20).
  • Postgres graph store — always its own pool, with leaner defaults pool_size=2 and max_overflow=20 (up to 22 connections). Under access control it is also created per dataset.
So a single-user setup with GRAPH_DATABASE_PROVIDER="postgres" can reach roughly 40 + 22 connections at peak, and backend access control multiplies the per-dataset pools by the number of datasets.Shrink the pools to fit the server’s max_connections. POOL_ARGS applies to the relational engine and is reused by the Postgres graph engine; VECTOR_POOL_ARGS applies to per-dataset PGVector engines. Both take a JSON object:
To minimize idle connections entirely, disable pooling so each operation opens and closes its own connection:
Alternatively, route application traffic through Neon’s pooled (-pooler) endpoint, which supports far more concurrent clients — but disable asyncpg’s prepared-statement cache when doing so (see the asyncpg prepared-statement / connection-pooler errors accordion above), and keep setup and migrations on the direct endpoint.
For Postgres, the database named in DB_NAME must already exist before Cognee connects. Unlike SQLite, Cognee does not issue CREATE DATABASE for Postgres — it connects directly to DB_NAME and creates only the tables. If the database itself is missing, create it once with your Postgres tooling:
(The built-in Docker Postgres service from docker compose --profile postgres up -d already creates this database for you.)If you specifically see DatabaseNotCreatedError (“The database has not been created yet. Please call await setup() first.”), Cognee reached Postgres but its tables (e.g. principals) don’t exist yet. Run setup once to initialize the schema:
remember() creates the tables automatically through its underlying add() and cognify() steps, so this typically only surfaces when calling search() or recall() first on a fresh database.

When to Use Each

  • SQLite: Local development, single-user applications, simple deployments
  • Postgres: Production environments, multi-user applications, external hosting, co-location with pgvector
  • Turso (libSQL): A SQLite drop-in when you want a hosted, replicated database — the same aiosqlite driver and Alembic migrations apply unchanged, with optional embedded-replica sync against a remote Turso primary

Vector Stores

Configure vector databases for embedding storage

Graph Stores

Set up graph databases for knowledge graphs

Overview

Return to setup configuration overview