> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cognee.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Docker Deployment

> Deploy Cognee and its supporting services using Docker Compose profiles

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](https://docs.docker.com/get-docker/) and Docker Compose v2+
* Git

## Quick Start

```bash theme={null}
git clone https://github.com/topoteretes/cognee.git
cd cognee
cp .env.template .env
```

Edit `.env` and set your LLM API key:

```bash theme={null}
LLM_API_KEY="your_api_key"
```

Then start the Cognee API server (no profile needed):

```bash theme={null}
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:

```bash theme={null}
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](/api-reference/introduction#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:

```bash theme={null}
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

<AccordionGroup>
  <Accordion title="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                                                                          |
  </Accordion>

  <Accordion title="Data Persistence and Host Files">
    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 area         | Role                                                                                                                                                         | Persistence in the checked-in compose file                                                            |
    | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
    | `redis`              | [Session/conversation cache](/core-concepts/sessions-and-caching)                                                                                            | Uses the mounted `redis_data` named volume                                                            |
    | `postgres`           | Relational metadata/state ([SQLite](/setup-configuration/relational-databases) 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](/setup-configuration/graph-stores) 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](/setup-configuration/vector-stores) for embeddings when `VECTOR_DB_PROVIDER=chromadb`                                                         | Persist 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](#postgresql-neo4j) and [PermissionError with External Databases](#permissionerror-external-databases) under for volume examples.

    To ingest files from your host machine, uncomment and update the volume in `docker-compose.yml`.

    ```yaml theme={null}
    # - /path/to/your/data:/data
    ```
  </Accordion>

  <Accordion title="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](/core-concepts/further-concepts/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](/setup-configuration/overview).
  </Accordion>

  <Accordion title="Common setups">
    <AccordionGroup>
      <Accordion title="Cognee + PostgreSQL">
        PostgreSQL with pgvector is a good production choice for the relational database.

        Add to your `.env`:

        ```bash theme={null}
        DB_PROVIDER=postgres
        DB_HOST=postgres
        DB_PORT=5432
        DB_USERNAME=cognee
        DB_PASSWORD=cognee
        DB_NAME=cognee_db
        ```

        Start both services:

        ```bash theme={null}
        docker compose --profile postgres up --build
        ```
      </Accordion>

      <Accordion title="Cognee + PostgreSQL + Neo4j" id="postgresql-neo4j">
        For production deployments with a dedicated graph database:

        Add to your `.env`:

        ```bash theme={null}
        # 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:

        ```yaml theme={null}
        services:
          postgres:
            volumes:
              - postgres_data:/var/lib/postgresql/data

          neo4j:
            volumes:
              - neo4j_data:/data

        volumes:
          postgres_data:
          neo4j_data:
        ```

        Start the stack:

        ```bash theme={null}
        docker compose --profile postgres --profile neo4j up --build
        ```

        Neo4j browser is available at `http://localhost:7474`.
      </Accordion>

      <Accordion title="Cognee + ChromaDB">
        Use ChromaDB as the vector store:

        Add to your `.env`:

        ```bash theme={null}
        VECTOR_DB_PROVIDER=chromadb
        VECTOR_DB_URL=http://chromadb:8000
        VECTOR_DB_KEY=your_chroma_token
        ```

        Start:

        ```bash theme={null}
        docker compose --profile chromadb up --build
        ```
      </Accordion>

      <Accordion title="Cognee + MCP Server">
        Run the [MCP server](/cognee-mcp/mcp-overview) alongside the API:

        ```bash theme={null}
        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`.
      </Accordion>

      <Accordion title="Cognee + Web UI">
        The `ui` profile starts the same web interface that [`cognee.start_ui()`](/cognee-cloud/local-ui) launches locally — here it runs as a separate `frontend` container:

        ```bash theme={null}
        docker compose --profile ui up --build
        ```

        The backend API and the UI listen on **different ports**, so they don't conflict:

        | Service                | URL                     | Port   |
        | ---------------------- | ----------------------- | ------ |
        | API backend (`cognee`) | `http://localhost:8000` | `8000` |
        | Web UI (`frontend`)    | `http://localhost:3000` | `3000` |

        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.

        <Note>
          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()`](/cognee-cloud/local-ui) for non-Docker, local Python setups.
        </Note>
      </Accordion>
    </AccordionGroup>
  </Accordion>

  <Accordion title="Managing the Docker Deployment">
    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:

    ```bash theme={null}
    # 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:

    ```bash theme={null}
    docker compose up -d --force-recreate cognee
    ```

    You only need `--build` when you change the `Dockerfile` or its dependencies (for example, [adding optional extras](#additional-information)) — not for `.env` edits:

    ```bash theme={null}
    docker compose up --build cognee
    ```

    <Note>
      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.
    </Note>

    Stop or remove containers with Docker Compose:

    ```bash theme={null}
    # Stop containers (preserves volumes)
    docker compose down

    # Stop and remove volumes (deletes all data)
    docker compose down --volumes
    ```
  </Accordion>

  <Accordion title="Optional Extras and Document Loaders">
    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](/getting-started/installation#extras-and-common-installation-combinations).
    For a table of supported file types and their loaders, see [Loaders](/core-concepts/further-concepts/loaders#supported-file-extensions).

    Example: adding the `docs` extra for [UnstructuredLoader](/core-concepts/further-concepts/loaders#external-loaders), office documents (`.docx`, `.pptx`, `.xlsx`, `.epub`, and similar formats), and `AdvancedPdfLoader`:

    ```dockerfile theme={null}
    # 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):

    ```dockerfile theme={null}
    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`:

    ```bash theme={null}
    docker compose up --build cognee
    ```
  </Accordion>

  <Accordion title="Bytecode Precompilation">
    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:

    ```dockerfile theme={null}
    # ENV UV_COMPILE_BYTECODE=1
    ```

    then rebuild:

    ```bash theme={null}
    docker compose up --build cognee
    ```
  </Accordion>

  <Accordion title="Troubleshooting">
    <AccordionGroup>
      <Accordion title="PermissionError with External Databases" id="permissionerror-external-databases">
        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**:

        ```yaml theme={null}
        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:

        ```yaml theme={null}
        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):

        ```yaml theme={null}
        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](/setup-configuration/overview#storage-amp-logging) for the related env vars, or [S3 storage](/guides/s3-storage) if you want to point these directories at S3 instead of local volumes.
      </Accordion>

      <Accordion title="PostgreSQL Connection Refused">
        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`:**

        ```yaml theme={null}
        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.

        ```bash theme={null}
        COGNEE_SKIP_CONNECTION_TEST=true
        ```
      </Accordion>
    </AccordionGroup>
  </Accordion>
</AccordionGroup>

<Card title="Need help?" href="https://discord.gg/m63hxKsp4p" icon="discord">
  Join our community for Docker deployment support.
</Card>
