Skip to main content
Deploy Cognee locally or on a server with Docker Compose. The included docker-compose.yml uses profiles so you can start only the services you need.

Prerequisites

  • Docker and Docker Compose v2+
  • Git

Quick Start

git clone https://github.com/topoteretes/cognee.git
cd cognee
cp .env.template .env
Edit .env and set your LLM API key:
LLM_API_KEY="your_api_key"
Then start the Cognee API server (no profile needed):
docker compose up --build cognee
The API will be available at http://localhost:8000. Interactive docs at http://localhost:8000/docs.

Verify Deployment

After the server starts, check that the API process is reachable:
curl -f http://localhost:8000/health
This only proves that the server is alive. It does not prove that ingestion, graph building, vector search, or LLM-backed recall works.

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:
printf "Cognee turns data into searchable AI memory." > /tmp/cognee-smoke.txt

curl -X POST http://localhost:8000/api/v1/remember \
  -F "data=@/tmp/cognee-smoke.txt" \
  -F "datasetName=smoke_test" \
  -F "run_in_background=false"

curl -X POST http://localhost:8000/api/v1/recall \
  -H "Content-Type: application/json" \
  -d '{"query": "What does Cognee do?", "datasets": ["smoke_test"], "search_type": "GRAPH_COMPLETION", "top_k": 5}'

Additional Information

Each optional service is gated behind a profile. Use --profile to activate one or more:
ProfileServicePort(s)Purpose
(none)cognee8000, 5678Core API server
mcpcognee-mcp8001, 5679MCP server for IDE integrations (host ports; container still listens on 8000/5678)
uifrontend3000Experimental web UI
neo4jneo4j7474, 7687Neo4j graph database
chromadbchromadb3002ChromaDB vector database
postgrespostgres5432PostgreSQL + pgvector
redisredis6379Redis caching
The compose file mounts your local 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 areaRolePersistence in the checked-in compose file
redisSession/conversation cacheUses the mounted redis_data named volume
postgresRelational metadata/state (SQLite by default, or Postgres), and vector store when VECTOR_DB_PROVIDER=pgvectorpostgres_data is declared, but the postgres service does not mount it yet
Embedded graph storeKnowledge 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
neo4jDedicated graph database when GRAPH_DATABASE_PROVIDER=neo4jRuns in its own service; add a Neo4j data volume for durability across container recreation
chromadbVector store for embeddings when VECTOR_DB_PROVIDER=chromadbPersist the Chroma data directory in the Chroma service you run
If 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.
# - /path/to/your/data:/data
The cognee container reads configuration from .env at startup. Key variables:
VariableDefaultDescription
LLM_API_KEY(required)API key for your LLM provider
LLM_MODELopenai/gpt-5-miniLLM model to use
DB_PROVIDERsqliteRelational DB: sqlite or postgres
GRAPH_DATABASE_PROVIDERkuzu in .env.templateGraph DB: kuzu, neo4j, etc. If unset, the application default is ladybug.
VECTOR_DB_PROVIDERlancedbVector DB: lancedb, chromadb, pgvector, etc.
CORS_ALLOWED_ORIGINS* in Docker ComposeRestrict to specific domains in production
HTTP_PORT8000Port the API server binds inside the container (entrypoint default)
BIND_ADDRESS0.0.0.0Address the API server binds inside the container (entrypoint default)
ENABLE_BACKEND_ACCESS_CONTROLTrueEnables per-user/dataset isolation. When this is True, authentication is required.
REQUIRE_AUTHENTICATIONInherits from ENABLE_BACKEND_ACCESS_CONTROL when unsetEnable JWT auth for the API. Setting this to False is ignored when ENABLE_BACKEND_ACCESS_CONTROL=True.
COGNEE_SKIP_CONNECTION_TESTfalseSkip LLM/embedding connectivity checks on startup. Accepts true, 1, or yes.
DEBUGfalseWhen true and ENV is dev or local, the container entrypoint starts under debugpy listening on DEBUG_PORT
DEBUG_PORT5678Port debugpy listens on when DEBUG=true
chunk_size1500Max tokens per chunk during cognify (see Chunkers)
chunk_overlap10Overlap 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.
PostgreSQL with pgvector is a good production choice for the relational database.Add to your .env:
DB_PROVIDER=postgres
DB_HOST=postgres
DB_PORT=5432
DB_USERNAME=cognee
DB_PASSWORD=cognee
DB_NAME=cognee_db
Start both services:
docker compose --profile postgres up --build
For production deployments with a dedicated graph database:Add to your .env:
# Relational DB
DB_PROVIDER=postgres
DB_HOST=postgres
DB_PORT=5432
DB_USERNAME=cognee
DB_PASSWORD=cognee
DB_NAME=cognee_db

# Graph DB
GRAPH_DATABASE_PROVIDER=neo4j
GRAPH_DATABASE_URL=bolt://neo4j:7687
GRAPH_DATABASE_NAME=neo4j
GRAPH_DATABASE_USERNAME=neo4j
GRAPH_DATABASE_PASSWORD=pleaseletmein
Add volumes for database durability across container recreation:
services:
  postgres:
    volumes:
      - postgres_data:/var/lib/postgresql/data

  neo4j:
    volumes:
      - neo4j_data:/data

volumes:
  postgres_data:
  neo4j_data:
Start the stack:
docker compose --profile postgres --profile neo4j up --build
Neo4j browser is available at http://localhost:7474.
Use ChromaDB as the vector store:Add to your .env:
VECTOR_DB_PROVIDER=chromadb
VECTOR_DB_URL=http://chromadb:8000
VECTOR_DB_KEY=your_chroma_token
Start:
docker compose --profile chromadb up --build
Run the MCP server alongside the API:
docker compose --profile mcp up --build cognee-mcp
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.
The ui profile starts the same web interface that cognee.start_ui() launches locally — here it runs as a separate frontend container:
docker compose --profile ui up --build
The backend API and the UI listen on different ports, so they don’t conflict:
ServiceURLPort
API backend (cognee)http://localhost:80008000
Web UI (frontend)http://localhost:30003000
The frontend’s local API client defaults to 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.
The 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:
# Re-reads .env and restarts the cognee process
docker compose restart cognee
If you changed the docker-compose.yml definition itself (ports, volumes, environment:, profiles), recreate the container instead so the new settings take effect:
docker compose up -d --force-recreate cognee
You only need --build when you change the Dockerfile or its dependencies (for example, adding optional extras) — not for .env edits:
docker compose up --build cognee
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.
Stop or remove containers with Docker Compose:
# Stop containers (preserves volumes)
docker compose down

# Stop and remove volumes (deletes all data)
docker compose down --volumes
The default Docker image includes a fixed set of extras from the repository 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:
# First uv sync (--no-install-project):
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --extra debug --extra api --extra postgres --extra neo4j \
             --extra llama-index --extra ollama --extra mistral --extra groq \
             --extra anthropic --extra chromadb --extra docs \
             --frozen --no-install-project --no-dev --no-editable

# Second uv sync (installs the project):
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --extra debug --extra api --extra postgres --extra neo4j \
             --extra llama-index --extra ollama --extra mistral --extra groq \
             --extra anthropic --extra chromadb --extra docs \
             --frozen --no-dev --no-editable
This same pattern works for other extras such as 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):
RUN apt-get update && apt-get install -y \
    libpq5 \
    curl \
    poppler-utils \
    tesseract-ocr \
    && rm -rf /var/lib/apt/lists/*
Rebuild after updating the Dockerfile:
docker compose up --build cognee
The repository 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:
# ENV UV_COMPILE_BYTECODE=1
then rebuild:
docker compose up --build cognee
Even when Cognee is configured to use external databases (Postgres, pgvector, Neo4j, etc.), local writable paths are still required. 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:
PermissionError: [Errno 13] Permission denied: '/app/cognee/.data_storage/...'
Fix — mount writable volumes for both directories:
services:
  cognee:
    image: cognee/cognee:main
    volumes:
      - cognee_data:/app/cognee/.data_storage
      - cognee_system:/app/cognee/.cognee_system
    environment:
      DB_PROVIDER: postgres
      # ... remaining DB / graph / vector settings

volumes:
  cognee_data:
  cognee_system:
If you relocate the storage paths with DATA_ROOT_DIRECTORY and SYSTEM_ROOT_DIRECTORY, mount the volumes at the same paths:
services:
  cognee:
    image: cognee/cognee:main
    volumes:
      - cognee_data:/var/cognee/data
      - cognee_system:/var/cognee/system
    environment:
      DATA_ROOT_DIRECTORY: /var/cognee/data
      SYSTEM_ROOT_DIRECTORY: /var/cognee/system
      DB_PROVIDER: postgres
      # ... remaining DB / graph / vector settings

volumes:
  cognee_data:
  cognee_system:
Working Postgres + pgvector + Neo4j compose example — includes healthchecks on both 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):
services:
  postgres:
    image: pgvector/pgvector:pg17
    environment:
      POSTGRES_USER: cognee
      POSTGRES_PASSWORD: cognee
      POSTGRES_DB: cognee_db
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U cognee -d cognee_db"]
      interval: 10s
      timeout: 5s
      retries: 5

  neo4j:
    image: neo4j:5.26
    environment:
      NEO4J_AUTH: neo4j/pleaseletmein
    healthcheck:
      test: ["CMD-SHELL", "cypher-shell -u neo4j -p pleaseletmein 'RETURN 1'"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 30s

  cognee:
    image: cognee/cognee:main
    depends_on:
      postgres:
        condition: service_healthy
      neo4j:
        condition: service_healthy
    volumes:
      - cognee_data:/app/cognee/.data_storage
      - cognee_system:/app/cognee/.cognee_system
    environment:
      DB_PROVIDER: postgres
      DB_HOST: postgres
      DB_PORT: 5432
      DB_USERNAME: cognee
      DB_PASSWORD: cognee
      DB_NAME: cognee_db
      VECTOR_DB_PROVIDER: pgvector
      GRAPH_DATABASE_PROVIDER: neo4j
      GRAPH_DATABASE_URL: bolt://neo4j:7687
      GRAPH_DATABASE_USERNAME: neo4j
      GRAPH_DATABASE_PASSWORD: pleaseletmein

volumes:
  cognee_data:
  cognee_system:
See Storage & Logging for the related env vars, or S3 storage if you want to point these directories at S3 instead of local volumes.
When Cognee starts before PostgreSQL finishes initializing, the first API call triggers LLM/embedding connectivity checks (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:
services:
  postgres:
    image: pgvector/pgvector:pg17
    environment:
      POSTGRES_USER: cognee
      POSTGRES_PASSWORD: cognee
      POSTGRES_DB: cognee_db
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U cognee -d cognee_db"]
      interval: 10s
      timeout: 5s
      retries: 5

  cognee:
    image: cognee/cognee:main
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      DB_PROVIDER: postgres
      DB_HOST: postgres
      DB_PORT: 5432
      DB_USERNAME: cognee
      DB_PASSWORD: cognee
      DB_NAME: cognee_db
This delays the 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.
COGNEE_SKIP_CONNECTION_TEST=true

Need help?

Join our community for Docker deployment support.