Graphiti in Production: FalkorDB or Neo4j, Azure or Ollama, and What Breaks

September 11, 2026 · 7 min read · knowledge-graph, graphiti, falkordb, production

Graphiti gets from notebook to demo in an afternoon. What takes longer is what the quickstart skips: a backend you can operate, a model that extracts reliably, embeddings that stay comparable, and ingestion you can afford at your volume. This is the checklist I work through, from running Graphiti for a company’s context layer on its own tenant and for my own working graphs.

1. Pick the backend

FalkorDBNeo4j
What it isGraph database as a Redis module, with sparse-matrix algebra (GraphBLAS) and OpenCypherGraphiti’s default backend, with Cypher and mature tooling
Graphiti requirementgraphiti-core[falkordb], FalkorDB 1.1.2 or newerNeo4j 5.26 or newer
Tenant isolationA separate named graph per database, plus group idsGroup ids inside one database; more databases need Enterprise
LicenseSSPL v1Community GPLv3, Enterprise commercial
OperationsOne container: port 6379 for clients, 3000 for the browserCommunity runs as a single instance; clustering, role-based access and online backup are Enterprise

Choose FalkorDB when you want a lean server and a hard wall between tenants, and your license policy accepts the SSPL, which some enterprise policies exclude the same way they exclude the AGPL. Choose Neo4j when the company already runs it or needs its tooling. Amazon Neptune works too, paired with OpenSearch Serverless for full-text search. The Kuzu driver is deprecated because the upstream project is no longer maintained, so do not start new work on it.

For FalkorDB:

docker run -p 6379:6379 -p 3000:3000 -it --rm falkordb/falkordb:latest
pip install "graphiti-core[falkordb]"
from graphiti_core import Graphiti
from graphiti_core.driver.falkordb_driver import FalkorDriver

driver = FalkorDriver(host="localhost", port=6379, database="acme")
graphiti = Graphiti(graph_driver=driver)

The database name selects a separate graph inside the same FalkorDB server, so each tenant or business domain can get its own. The container command is the quickstart one; in production, run FalkorDB with persistence and a volume.

2. Pick the model: structured output first

Graphiti extracts and deduplicates entities and facts through structured JSON output, and its README says plainly that it works best with providers that reliably honor it: OpenAI, Anthropic and Gemini. Every other choice needs a test before you trust it.

Azure OpenAI in the EU. Point Graphiti’s Azure clients at your resource’s OpenAI v1 endpoint, with one deployment for extraction and one for embeddings:

from openai import AsyncOpenAI
from graphiti_core import Graphiti
from graphiti_core.llm_client.azure_openai_client import AzureOpenAILLMClient
from graphiti_core.llm_client.config import LLMConfig
from graphiti_core.embedder.azure_openai import AzureOpenAIEmbedderClient

azure_client = AsyncOpenAI(
    base_url="https://your-resource.openai.azure.com/openai/v1/",
    api_key="...",
)
llm_client = AzureOpenAILLMClient(
    azure_client=azure_client,
    config=LLMConfig(model="your-extraction-deployment", small_model="your-small-deployment"),
)
embedder = AzureOpenAIEmbedderClient(
    azure_client=azure_client, model="your-embedding-deployment"
)

graphiti = Graphiti(graph_driver=driver, llm_client=llm_client, embedder=embedder)

Deploy both as Data Zone Standard in an EU region, and prompts and responses are processed inside the EU data zone. As of September 2026, OpenAI’s GPT-5.6 models are available that way in nine European regions, including Germany West Central and Sweden Central. Graphiti takes a model and a small_model; GPT-5.6 Terra for the first and Luna for the second is the pairing to test on your own episodes.

Ollama and other local models. Use OpenAIGenericClient, not OpenAIClient, pointed at Ollama’s OpenAI-compatible endpoint, with an Ollama embedder and the width set explicitly:

from graphiti_core import Graphiti
from graphiti_core.llm_client.config import LLMConfig
from graphiti_core.llm_client.openai_generic_client import OpenAIGenericClient
from graphiti_core.embedder.openai import OpenAIEmbedder, OpenAIEmbedderConfig
from graphiti_core.cross_encoder.openai_reranker_client import OpenAIRerankerClient

llm_config = LLMConfig(api_key="ollama", model="qwen2.5:7b", small_model="qwen2.5:7b",
                       base_url="http://localhost:11434/v1")
llm_client = OpenAIGenericClient(config=llm_config)
embedder = OpenAIEmbedder(config=OpenAIEmbedderConfig(
    api_key="ollama", embedding_model="nomic-embed-text", embedding_dim=768,
    base_url="http://localhost:11434/v1"))

graphiti = Graphiti(graph_driver=driver, llm_client=llm_client, embedder=embedder,
                    cross_encoder=OpenAIRerankerClient(client=llm_client, config=llm_config))

Small local models often return JSON that does not match the schema, which shows up as extraction failures. OpenAIGenericClient has a structured_output_mode: the default json_schema asks for native structured output, and json_object puts the schema into the prompt instead, which some local servers follow more reliably. Keep SEMAPHORE_LIMIT low for local servers, and check a few dozen extracted episodes by hand before you trust the model.

3. Embeddings: set the width, keep the model

graphiti-core reads its default vector width from the EMBEDDING_DIM environment variable, 1,024 when it is unset. Set embedding_dim in the embedder config and EMBEDDING_DIM in the environment of every process that imports graphiti_core, both to the width your model returns: 768 for nomic-embed-text. A mismatch does not raise an error. It writes vectors that match nothing.

Then keep the model. Query embeddings have to come from the same model, served the same way, that built the index. In my graphs, the same embedding model served by two different Ollama builds produced vectors with a cosine similarity of only 0.80 to each other, enough to lose matches. Record which embedder wrote the graph, and re-embed everything when you change it.

4. Pin versions: the reader equals the writer

Keep every process that reads a graph on the same graphiti-core version as the process that wrote it. In my graphs, a reader one patch version behind the writer returned no results at all, and raised no error. The official MCP server has its own version track, separate from graphiti-core, so pin both. When results go empty after an upgrade, check three things in this order: the versions, the embedder, and the group ids the query filters on.

5. Ingestion: cost, concurrency and proof it landed

  • Budget by episodes. Every episode costs several model calls for extraction and resolution, and the resolution context grows with the graph. Graphiti’s own issue tracker has threads on high ingestion cost and on resolution prompts that grow with the number of nodes. Estimate the cost per 1,000 episodes on a sample before you ingest a whole wiki.
  • Start with low concurrency. SEMAPHORE_LIMIT defaults to 10 to stay under provider rate limits. Raise it once you know your quota, and lower it when you see 429 errors.
  • Batch what can wait. A nightly ingest of a wiki space costs the same as a live one and is easier to watch.
  • Prove it landed. An issue on the Graphiti tracker reports that the MCP server’s add call can return success while an episode is silently dropped. Count episodes and facts after every ingest, and keep a ledger of what landed, so a rerun resumes instead of duplicating.

6. The write path and security

  • Keep write tools away from agents. The official MCP server exposes tools that add and delete episodes, delete facts and clear a whole group. Give agents read tools and put writes behind a person: the agent proposes, a person approves, a separate command applies.
  • Groups as an allow-list. Use group ids or FalkorDB databases to separate tenants and domains, and refuse reads and writes for groups a caller is not configured for.
  • Transport. Prefer stdio for local agents. Over HTTP, require a bearer token and keep the port off the public internet.
  • Telemetry. Graphiti sends anonymous usage telemetry by default. Set GRAPHITI_TELEMETRY_ENABLED=false where your policy requires it.

7. Backups and migrations

Take a snapshot before every upgrade and every embedder change. A snapshot written from Graphiti’s models rather than from backend rows restores into any backend, and restoring under a new embedder re-embeds every name and fact, which is also the clean path from one backend to another.

The checklist

  1. A backend chosen for your license policy and your operations team, with one graph or group per tenant.
  2. An extraction model that follows the schema on your own episodes, deployed where your data may go.
  3. embedding_dim and EMBEDDING_DIM set to your model’s width, and the embedder recorded.
  4. graphiti-core pinned, readers equal to writers, and the MCP server pinned separately.
  5. Ingestion budgeted per episode, concurrency tuned, every run counted and resumable.
  6. Agents on read-only tools, and writes approved by a person.
  7. A snapshot before every change.

Graphiti Local ships points 3, 4, 6 and 7 as defaults: kg doctor probes the embedding width, ingestion refuses an embedder change, the MCP tools cannot write, ingestion is resumable and a dry run by default, and kg export writes backend-neutral snapshots. For the concepts underneath, read the temporal knowledge graph guide; for the comparison with Mem0, Graphiti vs Mem0; and for a context layer built this way on your own tenant, knowledge graph consulting.

Graphiti Local is an independent community project built on Graphiti. It is not affiliated with or endorsed by Zep. Versions and defaults as of 11 September 2026.

Frequently asked questions

Should I run Graphiti on FalkorDB or Neo4j?

FalkorDB when you want a lean server, low latency and a separate named graph per tenant, and your license policy accepts the SSPL. Neo4j when your company already runs it, needs its tooling, or requires a license other than the SSPL; clustering and role-based access need the Enterprise edition.

How do I connect Graphiti to FalkorDB?

Install graphiti-core with the falkordb extra, start FalkorDB (the falkordb/falkordb image listens on port 6379, with a browser on port 3000), and pass a FalkorDriver with host, port and database name to Graphiti. The database name selects a separate graph, so each tenant can get its own.

Can Graphiti run with Ollama?

Yes, through the OpenAIGenericClient pointed at Ollama's OpenAI-compatible endpoint, with an Ollama embedder such as nomic-embed-text and the embedding dimension set to 768. Expect extraction failures with small models that do not follow the JSON schema, and test extraction on your own episodes before you trust it.

How do I use Graphiti with Azure OpenAI?

Point Graphiti's Azure OpenAI clients at your resource's OpenAI v1 endpoint, with one deployment for extraction and one for embeddings. With Data Zone Standard deployments in the EU, prompts and responses are processed inside the EU data zone.

Why does Graphiti return no results after an upgrade?

The usual causes are a version skew between the process that wrote the graph and the one reading it, an embedding model or width that differs from the one that built the index, or a group id filter that does not match. Pin graphiti-core, record the embedder, and check the group ids first.

What does Graphiti cost to run?

The graph database and the read path are cheap. Ingestion is the cost: every episode triggers several model calls for extraction and resolution, and the resolution context grows with the graph. Budget by episodes per day, keep concurrency low until you know your rate limits, and batch what can wait.

Book a 30-minute architecture call
The context layer for your AI agents

Your agents answer from whatever the retriever finds, and too often that is last quarter's truth. I build the context layer they answer and act from: a temporal knowledge graph that keeps every fact with its source and the time it held, reads with each person's own permissions, and writes nothing without a person's approval. On your own tenant, billed by the hour, step by step.

Get your AI pilot checked