docker-compose.yml uses profiles so you can start only the services you need.
Prerequisites
- Docker and Docker Compose v2+
- Git
Quick Start
.env and set your LLM API key:
http://localhost:8000. Interactive docs at http://localhost:8000/docs.
Verify Deployment
After the server starts, check that the API process is reachable:Smoke Test Ingestion and Recall
Docker users often test API routes immediately after startup. Cognee API endpoints use the versioned/api/v1 prefix, not plain /api; see API Base URLs for the full API reference note.
By default, ENABLE_BACKEND_ACCESS_CONTROL=True makes API authentication required. For a local unauthenticated smoke test, set ENABLE_BACKEND_ACCESS_CONTROL=false in .env and restart the container, or include a valid Bearer token in the curl requests.
Create a small file, ingest it synchronously, then query the same dataset:
Additional Information
Docker Compose Services
Docker Compose Services
Each optional service is gated behind a profile. Use
--profile to activate one or more:| Profile | Service | Port(s) | Purpose |
|---|---|---|---|
| (none) | cognee | 8000, 5678 | Core API server |
mcp | cognee-mcp | 8001, 5679 | MCP server for IDE integrations (host ports; container still listens on 8000/5678) |
ui | frontend | 3000 | Experimental web UI |
neo4j | neo4j | 7474, 7687 | Neo4j graph database |
chromadb | chromadb | 3002 | ChromaDB vector database |
postgres | postgres | 5432 | PostgreSQL + pgvector |
redis | redis | 6379 | Redis caching |
Data Persistence and Host Files
Data Persistence and Host Files
The compose file mounts your local
If
cognee/ source directory and .env file into the container. The database services each map to a distinct role, but only services with an active volumes: entry in docker-compose.yml persist data through container recreation by default:| Storage area | Role | Persistence in the checked-in compose file |
|---|---|---|
redis | Session/conversation cache | Uses the mounted redis_data named volume |
postgres | Relational metadata/state (SQLite by default, or Postgres), and vector store when VECTOR_DB_PROVIDER=pgvector | postgres_data is declared, but the postgres service does not mount it yet |
| Embedded graph store | Knowledge graph files under SYSTEM_ROOT_DIRECTORY (/app/cognee/.cognee_system) | Stored through the mounted source tree; mount a named volume for image-only or production deployments |
neo4j | Dedicated graph database when GRAPH_DATABASE_PROVIDER=neo4j | Runs in its own service; add a Neo4j data volume for durability across container recreation |
chromadb | Vector store for embeddings when VECTOR_DB_PROVIDER=chromadb | Persist the Chroma data directory in the Chroma service you run |
GRAPH_DATABASE_PROVIDER is unset, the application default graph provider is Ladybug. The repository .env.template currently sets Kuzu for Docker. Both are embedded file-based graph stores, so the graph files live under SYSTEM_ROOT_DIRECTORY unless you switch to a dedicated graph service.For a fully persistent Docker setup, either run Neo4j with --profile neo4j and set GRAPH_DATABASE_PROVIDER=neo4j, or keep the embedded file-based graph and mount a named volume over /app/cognee/.cognee_system. See Cognee + PostgreSQL + Neo4j and PermissionError with External Databases under for volume examples.To ingest files from your host machine, uncomment and update the volume in docker-compose.yml.Docker Environment Variables
Docker Environment Variables
The
cognee container reads configuration from .env at startup. Key variables:| Variable | Default | Description |
|---|---|---|
LLM_API_KEY | (required) | API key for your LLM provider |
LLM_MODEL | openai/gpt-5-mini | LLM model to use |
DB_PROVIDER | sqlite | Relational DB: sqlite or postgres |
GRAPH_DATABASE_PROVIDER | kuzu in .env.template | Graph DB: kuzu, neo4j, etc. If unset, the application default is ladybug. |
VECTOR_DB_PROVIDER | lancedb | Vector DB: lancedb, chromadb, pgvector, etc. |
CORS_ALLOWED_ORIGINS | * in Docker Compose | Restrict to specific domains in production |
HTTP_PORT | 8000 | Port the API server binds inside the container (entrypoint default) |
BIND_ADDRESS | 0.0.0.0 | Address the API server binds inside the container (entrypoint default) |
ENABLE_BACKEND_ACCESS_CONTROL | True | Enables per-user/dataset isolation. When this is True, authentication is required. |
REQUIRE_AUTHENTICATION | Inherits from ENABLE_BACKEND_ACCESS_CONTROL when unset | Enable JWT auth for the API. Setting this to False is ignored when ENABLE_BACKEND_ACCESS_CONTROL=True. |
COGNEE_SKIP_CONNECTION_TEST | false | Skip LLM/embedding connectivity checks on startup. Accepts true, 1, or yes. |
DEBUG | false | When true and ENV is dev or local, the container entrypoint starts under debugpy listening on DEBUG_PORT |
DEBUG_PORT | 5678 | Port debugpy listens on when DEBUG=true |
chunk_size | 1500 | Max tokens per chunk during cognify (see Chunkers) |
chunk_overlap | 10 | Overlap between chunks in words (only affects LangchainChunker) |
ENVIRONMENT is a deprecated alias for ENV, still accepted by the container entrypoints — prefer ENV.See the full list of options in Setup Configuration.Common setups
Common setups
Cognee + PostgreSQL
Cognee + PostgreSQL
PostgreSQL with pgvector is a good production choice for the relational database.Add to your Start both services:
.env:Cognee + PostgreSQL + Neo4j
Cognee + PostgreSQL + Neo4j
For production deployments with a dedicated graph database:Add to your Add volumes for database durability across container recreation:Start the stack:Neo4j browser is available at
.env:http://localhost:7474.Cognee + ChromaDB
Cognee + ChromaDB
Use ChromaDB as the vector store:Add to your Start:
.env:Cognee + MCP Server
Cognee + MCP Server
Run the MCP server alongside the API:The MCP server uses SSE transport and is published on host port
8001 (the container itself still listens on 8000, so the mcp profile doesn’t collide with the cognee API service when both run). Configure your IDE to point to http://localhost:8001/sse. The debugger is published on host port 5679.Cognee + Web UI
Cognee + Web UI
The The backend API and the UI listen on different ports, so they don’t conflict:
The frontend’s local API client defaults to
ui profile starts the same web interface that cognee.start_ui() launches locally — here it runs as a separate frontend container:| Service | URL | Port |
|---|---|---|
API backend (cognee) | http://localhost:8000 | 8000 |
Web UI (frontend) | http://localhost:3000 | 3000 |
http://localhost:8000. Keep the API published on port 8000 for the default Compose setup. If your API is reachable at a different host or port, pass NEXT_PUBLIC_LOCAL_API_URL to the frontend container with that backend URL.Don’t also call
cognee.start_ui() while the ui profile is running — both bind port 3000, so the second will fail with a “port already in use” error. In a Docker deployment use the ui profile; reserve cognee.start_ui() for non-Docker, local Python setups.Managing the Docker Deployment
Managing the Docker Deployment
The If you changed the You only need Stop or remove containers with Docker Compose:
cognee container reads .env once at startup, so edits to .env are not picked up by a running container. Restart the service to apply them:docker-compose.yml definition itself (ports, volumes, environment:, profiles), recreate the container instead so the new settings take effect:--build when you change the Dockerfile or its dependencies (for example, adding optional extras) — not for .env edits:Your
.env and the cognee/ source directory are bind-mounted into the container, so a restart is enough to apply config changes — no rebuild required.Optional Extras and Document Loaders
Optional Extras and Document Loaders
The default Docker image includes a fixed set of extras from the repository This same pattern works for other extras such as Rebuild after updating the
Dockerfile. If you need features behind another optional dependency, add the matching --extra <name> flag to both uv sync lines in the Dockerfile, then rebuild the image.For a table of available extras and common combinations, see Installation.
For a table of supported file types and their loaders, see Loaders.Example: adding the docs extra for UnstructuredLoader, office documents (.docx, .pptx, .xlsx, .epub, and similar formats), and AdvancedPdfLoader:scraping, redis, tracing, monitoring, or docling.For layout-aware or OCR-based PDF extraction with AdvancedPdfLoader, you also need poppler-utils and tesseract-ocr in the runtime stage of your Dockerfile (the second FROM python:3.12-slim-bookworm block):Dockerfile:Bytecode Precompilation
Bytecode Precompilation
The repository then rebuild:
Dockerfile sets ENV UV_COMPILE_BYTECODE=1, so uv sync compiles the virtual environment to .pyc bytecode at build time instead of leaving the interpreter to recompile each module from source on first import.The effect is faster container cold starts: without it the shipped venv contains no .pyc files, so every cold start recompiles the dependency tree from source. On the cognee-saas-pod image this accounted for roughly 8s of a ~13s import — about half the startup time.Trade-offs: builds take slightly longer and the image is marginally larger because the .pyc files are written into the venv layer.To disable it (for example to debug or reproduce from-source import behavior), comment out or remove the line in the Dockerfile before building:Troubleshooting
Troubleshooting
PermissionError with External Databases
PermissionError with External Databases
Even when Cognee is configured to use external databases (Postgres, pgvector, Neo4j, etc.), local writable paths are still required. Fix — mount writable volumes for both directories:If you relocate the storage paths with Working Postgres + pgvector + Neo4j compose example — includes healthchecks on both See Storage & Logging for the related env vars, or S3 storage if you want to point these directories at S3 instead of local volumes.
DATA_ROOT_DIRECTORY (default .data_storage) and SYSTEM_ROOT_DIRECTORY (default .cognee_system) hold ingestion artifacts, file caches, and loader outputs — they are not bypassed by pointing the relational, vector, or graph backends elsewhere.Inside the container these resolve to /app/cognee/.data_storage and /app/cognee/.cognee_system. If that path is read-only or owned by another user, ingestion fails with:DATA_ROOT_DIRECTORY and SYSTEM_ROOT_DIRECTORY, mount the volumes at the same paths:postgres and neo4j so Cognee does not start before either database is ready (Cognee otherwise races Neo4j’s Bolt listener and exits with a connection error):PostgreSQL Connection Refused
PostgreSQL Connection Refused
When Cognee starts before PostgreSQL finishes initializing, the first API call triggers LLM/embedding connectivity checks (This delays the
setup_and_check_environment) and may hit the database before it accepts connections, producing [Errno 111] Connection refused or [Errno 99] Cannot assign requested address.Recommended fix — add a healthcheck and depends_on condition to your docker-compose.yml:cognee container until PostgreSQL passes its health check.Alternative fix — bypass the connectivity check:If you cannot modify the compose file (e.g. third-party orchestration), set COGNEE_SKIP_CONNECTION_TEST=true to skip the LLM/embedding startup probe entirely. The check is only performed once (on first run), so the trade-off is that misconfigured endpoints are not caught until the first real request.Need help?
Join our community for Docker deployment support.