Why Your Local RAG Crashes With Lots of Files (& How to Fix It)
Z
Zack Saadioui
8/12/2025
Ah, the local RAG setup. It’s the dream, right? Running your own private, powerful AI on your machine, feeding it all your documents, and having it become your personal oracle. It feels like living in the future. Until, that is, you try to feed it a few hundred—or a few thousand—files. Then, the dream often turns into a nightmare of cryptic error messages, a fan that sounds like a jet engine, & a system that just… gives up.
If you’ve ever watched your local Retrieval-Augmented Generation (RAG) system sputter & crash after loading it up with data, you’re not alone. Honestly, it’s a super common rite of passage for anyone tinkering with local LLMs. You start with a few PDFs, it works like magic. You get ambitious, dump your entire "work" folder into it, & boom. Crash.
So, what’s actually going on under the hood? Why does something that seems so powerful on the surface buckle under the pressure of a few thousand text files? Turns out, it's not one single thing, but a perfect storm of hardware limits, software bottlenecks, & just plain inefficient strategies.
Let's break it down, get into the weeds, & figure out how to build a local RAG that can actually handle a heavy workload.
The Core Problem: Why Your Local RAG Is Throwing a Tantrum
At its heart, the issue is memory. But it’s a bit more complicated than just running out of RAM. We’re talking about a few different kinds of memory & resource constraints that all conspire against you when you start scaling up.
1. The VRAM Ceiling: Your GPU's Biggest Weakness
The single biggest bottleneck for most local AI work is Video RAM, or VRAM. This is the super-fast memory on your graphics card. For LLMs to run quickly, the model's weights (the "brain" of the AI) need to be loaded into VRAM. Consumer GPUs, even beefy ones like an RTX 4090, have a finite amount of it—maybe 12GB, 16GB, or 24GB if you're lucky.
Here's the kicker: it’s not just the model that eats up VRAM. The context window—the space where you load your documents for the RAG to "read"—also lives in VRAM. The more documents you stuff into the prompt, the more VRAM you consume. A study showed that you can often run a big model or use a long context, but rarely both at the same time on a consumer GPU.
When you load a massive number of files, your RAG system tries to create embeddings (numerical representations of your text) & potentially load large chunks of that text into the context. If the combined size of the model & the context data exceeds your VRAM, the system has to "spill" over to your regular system RAM. This is where performance absolutely tanks. You go from generating 50-100 tokens per second to a painful 2-5 tokens per second because the data has to travel across the much slower PCIe bus between your CPU & GPU. It’s like trying to sip a milkshake through a coffee stirrer.
Eventually, if the process is too demanding, it won’t just slow down—it’ll crash entirely.
2. System RAM & The "Too Many Open Files" Curse
Even if you manage your VRAM perfectly, you can still run into trouble with your regular system RAM. The process of loading, splitting, & creating embeddings for thousands of files is incredibly memory-intensive. Each file gets read into memory, processed, chunked, & then vectorized. If you’re not careful, your Python script can easily balloon in size & consume all available RAM, causing the operating system to kill the process.
A common, related error you might see in your logs is "Too many open files". This is an operating system-level limitation. Most systems have a cap on how many files a single process can have open at one time. When your RAG script tries to iterate over a massive directory of documents, it can easily hit this limit, leading to a crash before it even gets to the AI part of the process.
3. Inefficient Processing: The Naive RAG Pipeline
Let’s be honest, the default RAG pipeline you see in most tutorials is not built for scale. It usually goes something like this:
Load ALL documents from a folder.
Split ALL documents into chunks.
Create embeddings for ALL chunks.
Store ALL embeddings in an in-memory vector store.
This works great for a dozen files. For a thousand, it’s a recipe for disaster. This "all-at-once" approach is the primary software-level reason for crashes. It assumes you have infinite memory & processing power, which on a local machine, you MOST DEFINITELY do not.
How to Fix It: Building a Scalable Local RAG
Okay, so we know why it breaks. How do we fix it? It’s not about buying a supercomputer (though more VRAM always helps!). It’s about being smarter with our resources & building a more robust pipeline.
Step 1: Rethink Your Chunking Strategy
Chunking is the most critical & often overlooked part of a RAG pipeline. It's the process of breaking down large documents into smaller, manageable pieces. Bad chunking leads to poor retrieval & wasted resources.
The Problem with Default Chunking:
Most tutorials start with
1
Fixed-Size Chunking
. You just tell it "split the text every 1000 characters." This is simple, but terrible. You’ll split sentences in half, break up paragraphs, & generally destroy the semantic meaning of your text. A slightly better approach is
1
Recursive Character Text Splitting
, which tries to split on paragraphs, then sentences, etc. It’s better, but still fairly arbitrary.
Smarter Chunking Strategies for Large File Loads:
Semantic Chunking: This is the gold standard. Instead of splitting by character count, you split based on the meaning of the text. It uses an embedding model to measure the similarity between sentences. When the similarity drops off (meaning a new topic has started), it creates a new chunk. This results in chunks that are contextually coherent & much more effective for retrieval.
Document-Specific Chunking: Not all files are the same. A PDF report has a different structure than a Python script.
For PDFs/Reports: You should try to chunk based on sections, headings, or even tables. Respecting the document's structure preserves context.
For Code: Use a language-specific splitter that chunks based on functions or classes. Splitting a function in the middle is a great way to get useless results.
For General Text: Sentence-based chunking, where you group a certain number of sentences together, is a solid choice. It ensures you never break a sentence.
The Golden Rule of Chunking: Always add a bit of overlap. An overlap of 10-20% between chunks ensures that ideas that span across a chunk boundary aren't lost.
Step 2: Choose the Right Local Vector Database
When you're dealing with a few files, storing your vectors in a simple in-memory list is fine. When you have thousands of files, you have millions of vectors. Trying to hold all of that in active RAM is a surefire way to crash. You need a dedicated, disk-based vector store.
Here’s a quick comparison of popular local choices:
ChromaDB: This is often the starting point for many. It’s super easy to set up & integrates well with LangChain & LlamaIndex. It can run in-memory or persist to disk. For a large number of files, you MUST use its persistent mode. It's great for prototyping & small-to-medium projects, but can falter at a very large scale.
FAISS (Facebook AI Similarity Search): This is a library, not a full-fledged database. It is EXTREMELY fast & memory-efficient, especially if you have a GPU. FAISS is designed for high-performance similarity search on massive datasets. However, it’s a bit more complex to set up. It doesn’t store the original text, just the vectors, so you need a separate document store to map the retrieved vectors back to their content.
Qdrant: This is a more production-ready vector database that can be run locally via Docker. It's built in Rust & is highly optimized for performance & memory efficiency. It offers advanced features like filtering, which can be a lifesaver. Benchmarks often show it excelling in real-time filtered searches.
Recommendation for Local Scale: Start with ChromaDB in persistent mode. If you hit performance walls or need more speed, moving to FAISS (with a separate document store like SQLite) or a local Qdrant instance is the next logical step.
Step 3: Implement Hybrid Search
Relying purely on semantic (vector) search is a common failure point. While it’s great at understanding the meaning behind a query, it can sometimes miss exact keywords or specific terms, especially acronyms or product codes. Keyword search, on the other hand, is great at finding exact matches but has no semantic understanding.
The solution? Hybrid Search.
Hybrid search combines the best of both worlds:
Keyword Search (Sparse Retriever): Using an algorithm like BM25, which is a more advanced version of TF-IDF. It’s excellent at finding documents with specific, rare keywords from your query.
Semantic Search (Dense Retriever): Using your vector database (FAISS, Chroma, etc.) to find documents that are semantically similar to your query.
You then combine the results from both searches using a method like Reciprocal Rank Fusion (RRF) to get a final, much more relevant ranking. LangChain’s
1
EnsembleRetriever
makes this surprisingly easy to implement. You can create a BM25 retriever & a FAISS retriever & then combine them, assigning weights to each.
This approach is FAR more robust than relying on a single retrieval method & dramatically improves the quality of the context you provide to the LLM.
Step 4: Build an End-to-End Optimized Pipeline
Putting it all together, here’s what a robust, scalable local RAG pipeline looks like:
Don't Load Everything at Once: Instead of loading all files into memory, process them one by one or in small batches. Create a script that iterates through your file directory.
Load, Chunk, & Embed Incrementally: For each document, load it, apply your chosen smart chunking strategy (e.g., semantic chunking), & then create embeddings for those chunks.
Index as You Go: Add the chunks & their embeddings to your persistent vector store (like ChromaDB or FAISS) immediately after processing. Then, release the document from memory before loading the next one. This keeps your RAM usage low & stable.
Cache Your Embeddings: Re-computing embeddings for thousands of files every time you start your app is slow & wasteful. Store your embeddings! LangChain has a
1
CacheBackedEmbeddings
feature that lets you use a local file store as a cache. If the document hasn’t changed, it will pull the embedding from the cache instead of re-calculating it, saving a TON of time.
Separate Indexing from Querying: Your RAG application should have two modes: an "indexing" mode that you run once to process all your files, & a "query" mode that loads the pre-built index to answer questions. Don't re-index every time you ask a question.
Use a Hybrid Retriever for Queries: When a user asks a question, use your hybrid search setup (BM25 + vector search) to retrieve the most relevant chunks from your massive, pre-indexed knowledge base.
This approach transforms your RAG from a fragile, memory-hungry beast into a robust, efficient system that can handle a VAST number of files without breaking a sweat.
When Local Is No Longer Enough
Honestly, building & maintaining a local RAG system at scale is a significant amount of work. It’s a fun & rewarding project for a developer or a small team, but for a business, the complexity can quickly become a major distraction. You have to manage the hardware, optimize the pipeline, handle updates, & ensure it’s always running smoothly.
This is where managed solutions come into play. For instance, if you're a business looking to provide instant, AI-powered customer support, you'll quickly outgrow a local setup. You’ll need a system that's always on, can handle multiple users at once, & doesn't require an engineer to maintain it. This is where a platform like Arsturn becomes incredibly valuable. Arsturn helps businesses create custom AI chatbots trained on their own data—like all those documents you were trying to load locally. It provides instant customer support, answers questions, & engages website visitors 24/7, without you having to worry about VRAM, chunking strategies, or whether your local server is going to crash.
For businesses that need to generate leads or boost conversions, the goal is similar: automate engagement without the technical overhead. A tool like Arsturn lets you build no-code AI chatbots that are trained on your business's unique data, allowing for personalized customer experiences that feel authentic & helpful. It’s the power of a custom RAG system, but delivered as a reliable, scalable service.
Final Thoughts
Taming a local RAG system that has to deal with thousands of files is a fascinating engineering challenge. It forces you to move beyond the simple tutorials & think critically about every step of the pipeline—from how you split your documents to how you retrieve them. By adopting smarter chunking, using a persistent vector store, implementing hybrid search, & building an incremental indexing process, you can absolutely build a local RAG that scales.
But it’s also important to know when to move beyond a local setup. For personal use & learning, it's an amazing experience. For business applications, the reliability, scalability, & ease of use of a dedicated platform will almost always win out.
Hope this was helpful & gives you a clear path forward for your own RAG adventures. Let me know what you think, & good luck building