I sat in front of my terminal on a Tuesday in October 2025, staring at a spreadsheet. $39,600 per year. That was the number staring back at me — what it would cost to run the RAG system I’d just finished prototyping on OpenAI’s APIs. RAG, short for Retrieval Augmented Generation, is how you get an AI to answer questions about your own documents instead of just what it memorized from the internet.
I had a production deadline. I did not have forty grand.
So I rebuilt the whole thing around a local model — Phi-4, a 14-billion-parameter language model that runs entirely on my own GPU, with zero per-token charges — and a graph-based retrieval framework called LightRAG. The result: $2,300 a year. That’s not a typo. Ninety-four percent cheaper, same ballpark accuracy, sub-three-second query latency, running in production on AWS right now, chewing through thousands of documents for real business needs.
The savings came from moving inference onto my own hardware. The path there included a few production failures, and the economics of RAG are about to flip.
The number that stopped the project
The kill shot isn’t technical. It’s the budget meeting.
I’ve been in the room when it happens. Someone on the engineering team builds a genuinely impressive RAG demo — answers complex questions across the whole knowledge base, handles multi-hop reasoning (where answering one question requires connecting facts spread across multiple documents), the works. Everyone’s excited. Then someone asks what it costs to actually run.
For a typical enterprise setup — 50,000 documents and 1,000 queries a day — OpenAI’s pricing model looked like this:
- GPT-4 handling document processing and entity extraction: $1,500/month
- GPT-4 for generating query responses: $800/month
- Embedding API, the thing that turns text into searchable number vectors: $200/month
- Managed vector database (Pinecone or similar): $300/month
- Graph database (Neo4j Aura) for tracking entity relationships: $500/month
Total: $3,300/month. $39,600/year.
The bill climbs with every document and every query. The pricing model is structurally opposed to building a comprehensive knowledge system: you get punished for being thorough.
I watched projects get shelved over this exact math.
The replacement: a $6,000 GPU
Instead of paying per token, I bought a GPU server outright: $6,000 one time. That’s an NVIDIA RTX 4090 with 24GB of VRAM, 128GB of RAM, a 32-core CPU, and a 1TB NVMe drive. After that, the ongoing costs are electricity and bandwidth — about $190/month, or $2,300/year.
Year one total: $8,300. Break-even against the OpenAI approach: 2.3 months. After that, I’m saving $37,300 annually. Over three years, the total cost of ownership clocks in at $12,900 — an 89% reduction vs. the API route.
But cost is the least interesting reason to do this. The real payoff is what you can build when you are not watching a meter.
Four databases and a GPU
The system has four storage components. That was not the plan.
When I first opened LightRAG, my instinct was to simplify. “Surely we don’t need PostgreSQL and Neo4j and Redis,” I thought. So I tried running with just PostgreSQL.
It failed.
Tried with just Neo4j.
Failed again.
Each layer had a job the others could not fill.
PostgreSQL with pgvector is the vector search engine. When a query comes in, it gets converted to an embedding — a list of floating-point numbers representing its meaning in “semantic space.” PostgreSQL finds the document chunks whose embeddings sit closest to the query’s embedding. It also stores all the document content and metadata, and tracks whether each document has been processed yet.
Neo4j holds the knowledge graph, which makes multi-hop traversal possible. When someone asks “What security measures protect the patient data in our healthcare platform?”, answering that means following a chain: Healthcare Platform → uses Technology X → protected by Security Measure Y. That’s multi-hop traversal. Neo4j does it natively with graph queries. PostgreSQL would need recursive CTEs (a SQL technique that’s honestly painful for this). A flat vector store can’t do it at all.
Redis sits in front of everything as a cache. If someone asks the same question twice — or a near-identical one — Redis serves the cached answer and Phi-4 never runs. In production, this eliminates about 60% of redundant model calls.
Phi-4 via Ollama is the local language model. 14 billion parameters, meaning it has 14 billion numerical weights that determine its behavior — enough capacity for complex entity extraction and answer synthesis. It runs entirely on my infrastructure. Every query costs exactly nothing at the margin.
graph TB
subgraph Application["Application Layer"]
User[User Queries]
API[LightRAG API<br/>FastAPI Service]
end
subgraph Processing["Processing Engine"]
Pipeline[Document Pipeline]
QueryEngine[Query Engine<br/>Multi-hop Reasoning]
end
subgraph Storage["Storage Architecture"]
PG[(PostgreSQL<br/>pgvector<br/>Vector Search)]
Neo[(Neo4j<br/>Knowledge Graph<br/>Entity Relationships)]
Redis[(Redis<br/>Response Cache<br/>Processing Queue)]
Phi4[Phi-4 SLM<br/>14B Parameters<br/>128K Context Window]
end
User --> API
API --> Pipeline
API --> QueryEngine
Pipeline --> PG
Pipeline --> Neo
Pipeline --> Phi4
QueryEngine --> PG
QueryEngine --> Neo
QueryEngine --> Redis
QueryEngine --> Phi4
style User fill:#e1f5ff
style API fill:#b3e5fc
style Pipeline fill:#81d4fa
style QueryEngine fill:#81d4fa
style PG fill:#4db6ac
style Neo fill:#4db6ac
style Redis fill:#4db6ac
style Phi4 fill:#ffb74d
How the graph changes retrieval
Standard RAG is simple: take the user’s question, find chunks of text that look semantically similar, feed those chunks to a model, and ask it to synthesize an answer. This works fine for “What’s our refund policy?” It falls apart for anything that requires connecting dots across documents.
LightRAG does something smarter. During indexing, Phi-4 reads each document and pulls out entities — people, technologies, concepts, organizations — along with the relationships between them. Those go into Neo4j as a graph: nodes are entities, edges are relationships, and both get deduplicated and normalized so “Kubernetes” and “k8s” don’t become two separate nodes.
When a query arrives, the system combines two signals: vector similarity (finding text that means similar things) and graph traversal (following relationship chains through the knowledge graph). The retrieved context includes both relevant text chunks and the related entities with their connections.
Here is the kind of question that exposes the difference:
“What technologies does our platform use and what security protocols protect them?”
Naive RAG returns chunks that mention technologies and other chunks that mention security, but cannot connect them. You get two lists.
Graph RAG identifies “platform” as an entity, traverses its relationships to find connected technology entities, then traverses from those technology entities to security protocol entities. The response shows explicit connections — this technology is protected by that protocol — because the graph encoded those relationships during indexing.
The difference in answer quality is not subtle. Graph RAG produces structured, traceable answers that naive RAG simply cannot match.
What broke in production
Four things broke.
The context window was too small
LightRAG needs at least a 32K context window — meaning the model must be able to “see” up to 32,000 tokens of text at once — for entity extraction to work reliably. Ollama models default to 8K.
The failure mode was infuriating: cryptic “context length exceeded” errors during document processing. No indication that the fix was a one-line configuration change.
ollama pull phi4
ollama show --modelfile phi4 > Modelfile
echo "PARAMETER num_ctx 32768" >> Modelfile
ollama create -f Modelfile phi4-32k
ollama run phi4-32k
Or at the API level:
llm_config = {
"model": "phi4",
"options": {
"num_ctx": 32768,
"num_gpu": 1
}
}
If your entity extraction is failing, check this first. It will save you hours.
The GPU ran out of memory on large documents
Phi-4 14B needs roughly 18GB of VRAM for inference — the process of running the model to produce output. I was running it with quantization, which compresses the model weights to trade a tiny accuracy hit for a big memory reduction. But batch too many documents at once and you’ll still hit out-of-memory errors.
What worked: 4-bit or 8-bit quantization (the quality loss is negligible for entity extraction), conservative batch sizes calibrated to available VRAM, and nvidia-smi monitoring to catch memory pressure before it killed the process. I also wired in a fallback to CPU inference — painfully slow, but it means the pipeline doesn’t die when the GPU is saturated.
Query latency went from 2 seconds to 15+ after indexing 10,000 documents
The out-of-the-box database configs are tuned for development, not production. After 10,000 documents, my queries slowed to a crawl.
The fix was database tuning — memory allocation, index configuration, query planning hints. I’ll walk through the exact knobs in the Tech section below. After tuning: 3-5x improvement. Back to sub-3-second queries.
Documents fail in production. Constantly.
Network timeouts during ingestion. Corrupted PDFs. Phi-4 occasionally hallucinates invalid JSON during entity extraction, and the pipeline chokes. You need retry logic from day one.
LightRAG has built-in document status tracking — Pending → Processing → Completed/Failed — and I leaned on it hard. The web UI includes a “Retry Failed Documents” button with adaptive polling: 2 seconds during active processing, stretching to 30 seconds during idle periods. Every failure gets logged with the specific error, so you can pattern-match systemic issues vs. one-off corruption.
the mechanism — why this storage stack works give me the detail
The four-database layout isn’t aesthetic — each layer maps to a fundamentally different data structure that would be slow or impossible in the others.
PostgreSQL + pgvector stores float32 embedding vectors and uses an IVFFlat or HNSW index for approximate nearest-neighbor search. HNSW is the right default: it trades a larger index footprint for dramatically better recall at low ef_search values. Create it once after bulk ingestion (building on live data is much slower):
-- Enable the extension, then create an HNSW index on your embeddings column
CREATE EXTENSION IF NOT EXISTS vector;
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- At query time, tune recall vs. speed:
SET hnsw.ef_search = 40;Neo4j stores entity→relationship→entity triples extracted by Phi-4 during ingestion. The graph traversal that makes multi-hop reasoning fast is Cypher’s MATCH (a)-[:RELATES_TO*1..3]->(b) pattern — depth-bounded BFS that would require recursive CTEs in SQL and a full graph scan in a flat vector store.
Redis sits in front of both. LightRAG hashes the query + mode (local/global/hybrid) as a cache key. A 60 % cache-hit rate on repeated or near-identical queries means 60 % of your Phi-4 inference cycles never run.
The tuning knobs that actually move the needle (for a machine with 64 GB RAM and NVMe storage):
# postgresql.conf
shared_buffers = 8GB # ~12 % of RAM; OS page cache handles the rest
effective_cache_size = 24GB # planner hint, not actual allocation
work_mem = 256MB # per sort/hash op — high because pgvector sorts a lot
random_page_cost = 1.1 # NVMe is nearly sequential; default 4.0 kills index use
# neo4j.conf
dbms.memory.heap.max_size=16G
dbms.memory.pagecache.size=20G # page cache > heap for graph-heavy workloads
dbms.jvm.additional=-XX:+UseG1GC # G1 keeps GC pauses predictable under sustained loadQuick smoke test: after indexing 1,000 documents, run EXPLAIN (ANALYZE, BUFFERS) on a similarity query and confirm Index Scan using … hnsw appears — if you see Seq Scan the index wasn’t built or ef_search is set below the index’s m value.
The AWS bill
I run production on AWS with each component mapped to a managed or self-managed service:
Component Local Development Production AWS
───────────────── ──────────────────── ──────────────────────
PostgreSQL Docker container RDS PostgreSQL
Neo4j Docker container EC2 + EBS (r6i.2xlarge)
Redis Docker container ElastiCache Redis
Phi-4 Inference Ollama on GPU EC2 G5 instance
LightRAG App Docker container EC2 or ECS Fargate
Document Storage Local filesystem S3 + EFS
The monthly AWS breakdown, running 24/7:
- RDS PostgreSQL with Multi-AZ: $560
- EC2 r6i.2xlarge for Neo4j (Reserved Instance): $300
- ElastiCache Redis HA cluster: $280
- EC2 G5.2xlarge for Phi-4 (Reserved Instance): $650
- ECS Fargate for LightRAG application: $180
- Supporting infrastructure (ALB, S3, CloudWatch): $250
Total: $2,220/month ($26,640/year).
Still 33% cheaper than OpenAI’s API approach, and you get things the API route can’t offer: no rate limits (you won’t get throttled when the model says you’ve sent too many tokens in a minute), complete data sovereignty (your documents never leave your infrastructure), customizable entity extraction, and fixed costs regardless of query volume.
On-premise costs
If you can deploy on your own hardware, the numbers get even starker:
- GPU server (RTX 4090, 24GB VRAM, 128GB RAM, 32-core CPU, 1TB NVMe): $6,000 one-time
- Annual electricity (24/7, 500W average at $0.12/kWh): $525
- Bandwidth: $600
- Maintenance and spares: $175
- Yearly opex: $1,300
Break-even vs. OpenAI: 1.8 months. Three-year total: $9,900 — a 75% reduction.
Phi-4 beside GPT-4
I benchmarked the system on a 500-document technical corpus with 50 evaluation questions spanning simple factual lookups to complex multi-hop reasoning:
| Metric | Phi-4 + LightRAG | GPT-4 API + Standard RAG |
|---|---|---|
| Answer accuracy | 87% | 92% |
| Multi-hop reasoning | 83% | 89% |
| Average query latency | 2.8 seconds | 1.2 seconds |
| Cost per 1,000 queries | $0 (after capex) | $12-18 |
| Rate limits | None | 10,000 TPM |
| Data sovereignty | Complete | None |
A few things worth sitting with:
-
Phi-4 hits 87% of GPT-4’s accuracy with zero marginal cost per query. For most enterprise use cases, a 5% accuracy gap is a rounding error compared to the budget implications.
-
Graph RAG dramatically outperforms naive RAG for both models — multi-hop reasoning improves by 40% or more. The architecture matters more than the model.
-
Phi-4’s latency is slower (2.8s vs. 1.2s), but when you factor in API network overhead and rate-limit queuing, the gap narrows in practice.
-
The cost advantage at scale is overwhelming. You cannot argue with free-at-the-margin.
When local is the right choice
I’m not a zealot about self-hosting. There are cases where paying OpenAI is the right call.
Local makes sense if you handle sensitive or regulated data — HIPAA, GDPR, trade secrets, or anything you legally cannot send to a third-party API; need predictable costs at scale, especially with 10,000+ documents and 1,000+ daily queries; want custom entity extraction, graph schemas, or retrieval strategies; have someone comfortable with PostgreSQL, Neo4j, and GPU infrastructure; and can spend 2-4 weeks on initial setup and hardening.
Keep the APIs if you need to ship tomorrow, cannot spare the setup time, have no DevOps bandwidth because nobody on the team wants to own infrastructure, process non-sensitive public data, prefer variable per-use pricing over fixed infrastructure costs, or require vendor SLAs with guaranteed uptime and support contracts.
For enterprises with serious document volumes and regulatory requirements, self-hosting makes overwhelming sense. For a startup prototyping on public data, pay the API bill and focus on product.
How I would roll it out
Week 1: prove it locally
git clone https://github.com/HKUDS/LightRAG.git
cd LightRAG
cp env.example .env
# Edit .env with secure passwords
docker compose up -d
Verify at http://localhost:9621/webui/. Upload 10-50 test documents. Run queries in all three modes: local, global, hybrid. You need a GPU with 24GB+ VRAM, or accept slower CPU-only inference. Budget 500GB+ of free disk.
Week 2: run the AWS pilot
Deploy RDS PostgreSQL with pgvector. Launch an EC2 r6i.2xlarge for Neo4j. Configure an ElastiCache Redis cluster. Spin up an EC2 G5.2xlarge with Ollama and Phi-4. Wire it all together inside a VPC with private subnets for the databases, an Application Load Balancer for the LightRAG app, and CloudWatch for logging and monitoring.
Then run 1,000 representative documents through it, fire 100 evaluation queries, and measure latency, accuracy, and actual costs against your acceptance criteria.
Weeks 3-4: harden it
Security: encryption at rest on all databases. AWS Secrets Manager for credentials — never hardcode them. API authentication via keys or JWT. Rate limiting so no single client can saturate the system. Audit logging for compliance.
Reliability: RDS Multi-AZ for high availability. Automated backups for PostgreSQL, Neo4j, and Redis. Health checks with auto-recovery. Test your disaster recovery procedure before you need it.
Monitoring: CloudWatch dashboards for query latency, throughput, error rates, and GPU utilization. Alerts on failures, latency spikes, and resource exhaustion. Distributed tracing across query paths so you can see exactly where the time goes.
Performance: Tune database memory settings. Configure connection pooling. Redis query caching. Neo4j indexes on common traversal patterns.
What the spreadsheet leaves out
Three-year totals, for the decision-maker who needs to see them lined up:
OpenAI API: $118,800 Self-hosted AWS: $79,920 (33% savings) Self-hosted on-premise: $12,900 (89% savings)
The spreadsheet matters. But the things it doesn’t capture matter more:
Data sovereignty is the first difference. Documents never leave your infrastructure. For healthcare, finance, and legal work, that removes whole categories of compliance overhead and legal exposure.
There is also no ceiling on experimentation. When every query costs money, you ration experiments. When queries are free at the margin, you can run A/B tests, try unusual retrieval strategies, and iterate without asking the budget for permission.
Costs are fixed at any scale. A product launch does not create a new conversation with finance, and processing 100,000 documents overnight does not create a surprise bill. The infrastructure cost is the infrastructure cost.
There is no provider throttling. OpenAI caps tokens per minute; with a model you control, you process at whatever rate the GPU can sustain.
What I am building next
Multi-modal document understanding. Right now LightRAG only handles text, but real enterprise documents are full of architecture diagrams, database schemas, screenshots, and performance charts. I’m experimenting with vision models — specifically LLaVA — to extract structured information from images before feeding it into the RAG pipeline. Early results on technical diagrams are promising.
Natural language to graph queries. Currently every question goes through the RAG retrieval pipeline. I want to also enable direct graph queries: “Show me every microservice that depends on the authentication service” → auto-generated Cypher query → interactive graph visualization. This exposes the full power of the knowledge graph for exploratory work, not just Q&A.
Continuous learning from feedback. LightRAG doesn’t learn from usage patterns. I’m building thumbs up/down on answers to surface weak areas, user corrections for entity relationships, an automated fine-tuning pipeline, and an A/B testing framework for prompt optimization. The goal: the knowledge base gets better every time someone uses it.
The Production Trade After Six Months
Local models are ready. Phi-4 handles complex RAG tasks that a year ago required GPT-4, and the performance gap is closing fast. It reached 87% of GPT-4 accuracy on a $6,000 box, a trade most enterprises should take.
Architecture beats model choice. Graph RAG dramatically outperforms naive RAG regardless of the model because multi-hop reasoning and entity relationships produce answers that vector similarity alone cannot. The 67-94% savings changes full knowledge systems from “nice to have” to a clear economic choice.
Data sovereignty isn’t theoretical: for regulated enterprises, keeping everything in-house removes entire risk categories, and that’s worth real money. The main barrier is setup time — 2-4 weeks to production — followed by 2-4 hours a month of ongoing maintenance.
Start With 1,000 Documents
Prove the concept locally with 1,000 documents before committing to production infrastructure. Benchmark it against your current solution or document set — don’t trust my numbers; measure yours — and build the cost model from your actual document counts and query volumes, not my averages.
Choose AWS vs. on-premise based on compliance requirements, not cost alone. Index one domain at a time, then expand iteratively. Don’t boil the ocean.
A $6,000 GPU, a local model, graph-based retrieval, and careful database tuning gets you a production-grade system at sustainable cost. Prohibitively expensive RAG is no longer the only production option.
I’m working on follow-up posts about fine-tuning Phi-4 for domain-specific entity extraction, optimizing Neo4j query performance on large graphs, and building production monitoring for self-hosted RAG.
Jon Roosevelt is an AI architect specializing in healthcare and enterprise systems. He builds production ML systems with a focus on performance, cost, and data sovereignty.