SQLite Is Enough for Local AI Agent Memory
Why most local multi-agent workflows don't need PostgreSQL for semantic memory — and when they actually do.
Building Wolbarg — local-first semantic memory for AI agents.
- SQLite
- AI memory
- AI agents
- local-first
- semantic memory
- vector search
When people build an AI application, the database choice looks obvious: PostgreSQL, maybe Redis for caching, maybe pgvector for semantic search. SQLite almost never makes the list.
That surprised me. While building and benchmarking a local-first semantic memory layer for AI agents, I kept ending up with a single file, no server, no connection pool, no Docker container just to remember what an agent said five minutes ago.
This post is about that decision, and why SQLite is often the better fit for local multi-agent workflows.
The Default Everyone Reaches For
Postgres wins by default because it's familiar — concurrent users handled, huge extension ecosystem, an answer for every edge case. So when you start storing AI memory, your brain reaches for the usual stack: spin up Postgres, add pgvector, wire up a connection string, hope your local setup matches production someday.
That path isn't wrong. It's just heavier than most local AI work needs.
Local agents usually run on one machine, one process, maybe a handful of agents sharing context. "Database" here just means durable storage with a way to search by meaning. You don't need a network daemon for that — you need something that starts fast, stays out of the way, and doesn't die when your laptop sleeps.
What Local Memory Actually Needs
Strip away the marketing and the list is short: low latency, simplicity (no migrations, users, or roles to wrestle with), local execution (offline works, no cloud bill), reliability (transactions, not half-written memories), and persistence (a file you can copy, back up, or delete).
Replication, horizontal scaling, multi-tenant connection pooling — those solve problems you don't have yet.
Why SQLite Fits
SQLite is a library, not a server. Your app opens a file, the engine runs inside your process, you close the file when you're done. That explains most of why it works here.
One file. Reset state by deleting it. Share a demo by zipping it. Inspect what an agent stored with any SQLite browser.
No server. No docker compose up, no service fighting you on an OS update, no "connection refused" at 1 a.m.
ACID without ceremony. A write that touches a memory row, its embedding, and a keyword index either fully lands or fully rolls back — you don't get "text saved, embedding missing" unless you design for that yourself. For semantic memory, text and embedding staying in sync isn't optional.
Fast startup, low overhead. No separate process warming caches. Opening a local store takes milliseconds, because there's no second runtime spinning up next to your app.
But Isn't SQLite Slow?
That reputation is outdated. SQLite is tuned C, battle-tested for decades across browsers, phones, and embedded systems. For workloads that fit on one machine — exactly what local agents are — it's often faster than a networked database, because network latency just disappears. A local SELECT reads from disk or the OS page cache; a Postgres query on localhost still goes through sockets and a separate process.
In my own benchmarks (mock embeddings, 1,000 memories), SQLite held up fine for a laptop multi-agent workflow:
| Metric | SQLite | PostgreSQL (localhost) |
|---|---|---|
| Cold startup | 7.9 ms | 53.0 ms |
| Insert throughput | ~1.7k ops/sec | ~289 ops/sec |
| Semantic search (1k) | 2.0 ms | 4.7 ms |
| Retrieval top-5 (1k) | 2.2 ms | 4.5 ms |
SQLite vs PostgreSQL on localhost. Same machine, same SDK path — the gap is mostly process + socket overhead, not raw query cleverness.
For semantic memory at this scale, the bottleneck is almost never SQLite — it's the embedding call, the model, or the agent loop waiting on tools.
Semantic Memory Without a Vector Database
Agents need to recall by meaning, not just keywords. "The user prefers dark mode and hates popups" should surface when you later ask "what UI preferences do we know?" — a plain keyword search might miss it.
Every memory becomes an embedding — a list of numbers where similar meanings end up close together. People assume that needs a specialized vector database. Locally, it doesn't. SQLite can store embeddings and search them directly; extensions like sqlite-vec add proper vector indexes, and where that's unavailable you fall back to blobs and in-process cosine similarity. Pair that with a keyword index (FTS5) and you get hybrid search — meaning plus exact tokens like error codes and IDs. None of it needs a server.
What About Multiple Agents?
The nervous question: if a researcher, writer, and critic agent all share memory, don't you need something that handles concurrent access properly?
Locally, usually not. You're not running hundreds of independent clients over a network — one process is coordinating a few agents that read often and write rarely. SQLite handles this well in WAL mode, where readers keep reading while a write happens instead of everyone queuing behind one lock. Writes still need care: SQLite allows one writer at a time, so a local SDK serializes writes with a mutex and wraps each insert in a transaction.
Is that a limitation? Yes. Is it a problem when writes are already bounded by LLM latency? Rarely — you're storing a handful of memories between reasoning steps, not doing 50,000 inserts a second from ten microservices.
When SQLite Stops Being Enough
Reach for Postgres when you have server-shaped problems: cloud SaaS with many concurrent users writing from different machines, distributed systems spanning multiple app servers or regions, replication and failover requirements, analytics over hundreds of millions of rows, or a team that's already standardized on Postgres operations.
The point isn't "never use PostgreSQL." It's: don't start there for a local AI agent just because it feels more professional. Complexity is a cost — pay it when the problem demands it.
| SQLite | PostgreSQL | |
|---|---|---|
| Setup | Open a file | Install, configure, connect |
| Deployment | Ships with the app | Separate service to run |
| Concurrency | One writer, many readers (WAL) | Many concurrent writers |
| Operations | None | Backups, replicas, access control |
| Best use case | Single machine, local agents | Multi-client, networked, production scale |
| Local-first experience | Native | Requires extra infrastructure |
A Practical Filter
Use SQLite when the app runs on one machine, agents share memory inside one process or a small local set, you want zero-ops setup, and your dataset looks more like "project knowledge" than "internet-scale corpus." Use Postgres when multiple remote clients need live shared memory, you already operate Postgres in production, or you need concurrent writers at a scale SQLite's single-writer model can't absorb.
Most local multi-agent workflows fall in the first bucket. People just don't notice, because every tutorial starts with Postgres.
Final Thought
SQLite isn't a compromise for local AI agent memory — for a lot of builders, it's the correct first architecture. Durable storage, transactions, fast local reads, semantic search with embeddings, shared memory across agents, all in a file you actually understand.
Postgres is excellent. It just solves a different problem than the one most local agents have. Start simple, store memory locally, and add a server database when real concurrency or distribution shows up — not before. You might find you were overengineering this the whole time.