Register now for early access to concurrent writes in the Turso Cloud. Join the waitlist
Most database scaling advice breaks when you're dealing with AI agents. Here's why giving each agent its own database works, and four patterns for building it on Turso.

Most database scaling advice breaks when you're dealing with AI agents.
Take the standard playbook: connection pooling, careful indexing, row-level security, maybe partition your tables. This works great when you're running one application serving many users. It falls apart when you're running 10,000 autonomous entities, each with their own state, their own unpredictable lifespan, and their own tendency to occasionally go rogue and corrupt data.
The standard solutions are variations on multi-tenancy. The most common is a schema per tenant on a shared server, and the other familiar route is a tenant_id column on everything, with some indexes and carefully written WHERE clauses. Both work, until you need to debug which agent wrote what, or agent 4,672 starts thrashing your indexes, or you realize deleting an agent means hunting through 47 tables to untangle which rows belong to it.
There's a different approach: give each agent its own database.
That sounds expensive. With Turso it isn't, because a database is a lightweight, isolated file rather than a running server process. Creating one is an API call, an idle one costs only the storage it consumes, and deleting one removes every trace of the agent in a single operation.
Agents behave nothing like users, and they don't really fit the microservice model either.
A microservice performs a bounded task: process payments, send emails, render images. Its data model is predictable. You know what tables it needs, roughly how much data it'll store, and when to clean things up.
Agents work differently. Each one accumulates context, conversation history, and learned preferences. Agent A's memory has nothing to do with Agent B's, yet in a shared database they're crammed into the same agent_memory table. You end up building elaborate soft-delete systems, cascade rules, and indexing strategies to separate data that never needed to be together in the first place.
Some agents exist for five minutes. Others run for months. Your schema doesn't know which is which, so you provision for theoretical maximums.
When an agent hallucinates and writes garbage, you want to delete its database and start over. In a shared schema, that means writing complex cleanup scripts and hoping you caught everything.
Every agent gets its own dedicated database. Separate at the database level, rather than carved out with a schema or a tenant_id column.
SQLite made this possible by showing that a database can be lightweight: a single file that's cheap to create and trivial to delete.
With Turso Cloud, you create databases programmatically through the Platform API:
import { createClient } from "@tursodatabase/api";
const turso = createClient({
token: process.env.TURSO_API_TOKEN,
org: "my-org",
});
// Spin up a database for a new agent
const database = await turso.databases.create(`agent-${agentId}`, {
group: "default",
});
Moments later, you have a dedicated database. When the agent is done:
await turso.databases.delete(`agent-${agentId}`);
It's gone. No orphaned rows, no cascade rules, no soft-delete columns lingering forever.
Once databases are cheap to create and delete, several patterns become practical.
The default pattern needs no special architecture at all. Each agent gets its own database in Turso Cloud, and your application connects to it over the network like any other database:
import { connect } from "@tursodatabase/serverless";
const agentDB = await connect({
url: agentDatabaseUrl, // from the Platform API when the agent was created
authToken: process.env.TURSO_AUTH_TOKEN,
});
const insert = await agentDB.prepare(
"INSERT INTO memories (content) VALUES (?)"
);
await insert.run([memory]);
This is the standard way to run database-per-agent in production. Your agents run anywhere, serverless functions included, and every agent's state lives in its own fully isolated cloud database. The patterns below build on this foundation for cases with special latency, offline, or coordination needs.
For agents that run locally, such as desktop apps, mobile apps, or on-device assistants, each agent gets an embedded database that lives on the device:
import { connect } from "@tursodatabase/database";
const agentDB = await connect(`./agents/agent-${agentId}.db`);
There's zero network latency and full offline capability, and the agent's entire state is one file you can copy, back up, or delete.
This works well for privacy-sensitive applications, where the agent's data never leaves the user's device.
When local agents need to persist beyond their environment, Turso Sync connects embedded databases with Turso Cloud:
import { connect } from "@tursodatabase/sync";
const agentDB = await connect({
path: "agent-local.db",
url: process.env.TURSO_DATABASE_URL,
authToken: process.env.TURSO_AUTH_TOKEN,
});
// Reads and writes happen locally
const insert = await agentDB.prepare(
"INSERT INTO memories (content) VALUES (?)"
);
await insert.run([memory]);
// Sync with the cloud in the background
await agentDB.push();
await agentDB.pull();
The agent feels instant because it reads and writes a local database, and the data stays durable because it syncs to Turso Cloud. This is local-first architecture applied to AI agents: it works offline and syncs when connected.
Some systems need both isolation and cross-agent coordination: individual databases per agent, plus a shared coordination database.
// Each agent has its own database
const agentDB = await createAgentDatabase(agentId);
// Shared coordination database
const coordinationDB = await connect({
url: process.env.TURSO_COORDINATION_URL,
authToken: process.env.TURSO_AUTH_TOKEN,
});
// Agent stores data in isolation
const store = await agentDB.prepare(
"INSERT INTO memories (content) VALUES (?)"
);
await store.run([memory]);
// Agent publishes status to the coordination layer
const publish = await coordinationDB.prepare(
"INSERT INTO agent_status (agent_id, status, updated_at) VALUES (?, ?, ?)"
);
await publish.run([agentId, "task_completed", Date.now()]);
Agents never touch each other's databases. They coordinate through a lightweight shared layer when they need to.
Turso has native vector types, so you can store embeddings directly alongside structured data:
CREATE TABLE memories (
id INTEGER PRIMARY KEY,
content TEXT,
embedding F32_BLOB(1536)
);
import { connect } from "@tursodatabase/database";
const agentDB = await connect(`./agents/agent-${agentId}.db`);
// Store a memory with its embedding
const store = await agentDB.prepare(
"INSERT INTO memories (content, embedding) VALUES (?, vector32(?))"
);
await store.run([text, JSON.stringify(embeddingArray)]);
// Query for similar memories
const search = await agentDB.prepare(
`SELECT content
FROM memories
ORDER BY vector_distance_cos(embedding, vector32(?))
LIMIT 5`
);
const results = await search.all([JSON.stringify(queryEmbedding)]);
Each agent's semantic memory stays in its own database. There's no cross-tenant vector search leakage and no separate vector database to manage.
Provisioning. Databases are created and deleted through the Platform API, fast enough to provision on demand as agents spin up. Embedded database creation is a local file operation.
Economics. Turso's Free tier includes 100 databases, and all paid plans include unlimited databases, starting with the Developer plan at $4.99/month. The billing model is what makes the pattern work: an idle database costs only the storage it consumes, with $0 across every other metric. And the deeper advantage sits just above idle. Any provider can make a fully idle database cost nothing, but agents are rarely fully idle or fully busy; they're almost idle, waking up for a handful of reads and writes at a time. Turso bills by the individual request, so 1,000 agent databases where 100 do light work this month cost you storage for 1,000 small files plus exactly the requests those 100 performed, with no per-instance charge anywhere.
Isolation. An agent's blast radius is its own database. A misbehaving agent can corrupt its own state and nothing else, debugging means opening one small database that contains only that agent's data, and compliance questions about data separation have an architectural answer instead of a policy one. Deleting an agent deletes everything it ever stored, in one operation.
Database-per-agent trades one set of problems for another.
Cross-agent queries. Analytics across all agents means you can't just run SELECT * FROM agents. You either query multiple databases or maintain a separate analytics database that aggregates the data. That's solvable, though it works differently from the shared-table approach.
Database-per-agent works well when:
It's a poor fit when:
The shift from "databases as shared infrastructure" to "databases as agent-scoped resources" is subtle but fundamental. It mirrors the shift that happened with containers, moving from "everyone shares the server" to "every service gets its own container." Isolation is often simpler than sharing.
Building an agentic system and finding that this resonates? Spin up a test agent database with Turso and see how it feels. The Platform API docs cover programmatic database creation in more detail.
Sign up for free at turso.tech.