Turso alternatives: libSQL hosting without row-read limits
Short version: if you want hosted libSQL without a row-read meter, Layerbase Cloud runs it at flat per-instance pricing and the standard @libsql/client connects to it with no code changes. Stay on Turso if you specifically want its embedded-replicas-at-the-edge model and you have audited your query patterns enough to predict the bill. If you are embedding a database in one application on one machine, plain SQLite is the right answer and you do not need libSQL at all.
libSQL is a fork of SQLite by the Turso team that adds the things SQLite always lacked: a network protocol, replication, and the ability to run as a server rather than just an embedded library. It's a great piece of work. The fact that the SQLite Consortium isn't accepting external contributions to SQLite proper means a fork like libSQL is the only realistic path to a more network-friendly SQLite-compatible engine, and Turso has invested heavily in making it good.
The hosting story is dominated by Turso (the company), and Turso is fine for plenty of workloads. Where it stops being fine is the moment you look at the row-read counter on your dashboard and realize your application is making a lot more database queries than you thought. Their pricing meters reads per row, not per request, and certain access patterns blow through the free and starter tiers faster than people expect. The recurring complaint on HN and elsewhere is "I thought this would be cheap and the bill surprised me."
If that resonates, this post is for you. Below: how libSQL hosting actually works in 2026, what each option costs you, and where the row-read trap shows up.
Contents
- The row-read pricing trap
- Option 1: Layerbase Cloud
- Option 2: Run it locally with the Layerbase CLI
- Option 3: Layerbase Desktop
- Option 4: Self-host libSQL
- Option 5: Plain SQLite
- Option 6: Cloudflare D1
- FAQ
- Which one to pick
The row-read pricing trap
Quick context for anyone who hasn't hit this yet. Turso's pricing meters two things: storage and "rows read." A row read is what it sounds like: every row your queries scan, including rows scanned to satisfy WHERE clauses that filter them out.
Some examples of queries that look cheap and aren't:
SELECT * FROM users WHERE email = 'x@example.com'on a table without an index onemail. Every row in the table gets scanned and counted as a read, even though you only return one.SELECT COUNT(*) FROM events WHERE created_at > '2026-01-01'. SQLite has to walk through enough of the table to count, which counts as reads.- Anything that does a full table scan for analytics-style queries. The scan is a lot of reads.
The official advice is to add indexes for every query pattern, which is good advice generally but means you need to be far more careful about access patterns than you'd be on a database with flat-rate pricing. The free tier covers a real chunk of usage, but applications with chatty query patterns can blow through it in unexpected ways.
This is fine if you're aware of it and design your queries accordingly. It's not fine if you assumed "SQLite is cheap" and the bill shows up at the end of the month.
Option 1: Layerbase Cloud
Layerbase Cloud hosts libSQL with flat per-instance pricing. No row-read meter. You pay for the database to exist, not for what your queries do inside it.
curl https://<your-host>.cloud.layerbase.dev/healthlibSQL on Layerbase runs the upstream libSQL server with TLS and HTTP-based access. You connect with the @libsql/client package, which is the standard libSQL client:
import { createClient } from '@libsql/client'
const client = createClient({
url: 'https://<your-host>.cloud.layerbase.dev',
authToken: '<your-token>',
})
const result = await client.execute(`SELECT * FROM users WHERE email = ?`, [
'x@example.com',
])
console.log(result.rows)The same client works against Turso, against a local libSQL server, and against Layerbase Cloud. No code changes when you switch.
What's different on Layerbase:
- Pricing is per instance, not per row read.
- Single-region by default. Turso's embedded-replicas-everywhere model is a real feature; we don't have an equivalent. If you need geo-distributed read replicas, Turso is the right answer.
- Scale-to-zero is on for free instances. libSQL cold-starts quickly.
When to pick Layerbase: you want a hosted libSQL with predictable pricing and you don't need geo-distributed replicas.
When to pick Turso: you specifically want the embedded-replicas-at-the-edge model and you've audited your queries enough to know what your row-read consumption will be.
Option 2: Run it locally with the Layerbase CLI
For local development, the Layerbase CLI (formerly SpinDB) runs a real libSQL server on your machine. Same binary as the upstream Turso project ships. (What is the Layerbase CLI?)
npm i -g layerbase # npm
pnpm add -g layerbase # pnpmCreate an instance:
lbase create libsql1 -e libsql --startGet the connection URL:
lbase url libsql1http://127.0.0.1:8080That's a real libSQL server with HTTP access. Point the @libsql/client package at it during development, then swap to Layerbase Cloud or Turso for production by changing the URL.
lbase stop libsql1
lbase start libsql1
lbase listA common pattern: develop against the Layerbase CLI's libSQL locally, deploy to Layerbase Cloud (or Turso, if that fits) for production. Same client code, just different connection details.
Option 3: Layerbase Desktop
Layerbase Desktop gives you the SpinDB experience in a desktop app. Useful for libSQL specifically because it gives you a stable place to manage local instances without remembering ports or auth tokens. New instance, pick libSQL, copy the URL.
If you're prototyping a SQLite-replacement architecture and want to keep a libSQL handy for quick experiments, Desktop is the easiest setup.
Option 4: Self-host libSQL
The libSQL server is open source. The repo is at github.com/tursodatabase/libsql and there's an sqld binary you can run on any VM:
sqld --http-listen-addr 0.0.0.0:8080 --grpc-listen-addr 0.0.0.0:5001You're responsible for TLS, auth tokens, backups, and replication if you want any. Doable, especially for a small workload. The big trade-off: libSQL's interesting features (embedded replicas, vector search, server-side migrations) require some infrastructure work to wire up correctly.
If you do go this route, set up off-host backups before you put anything important in the database. libSQL's WAL-based replication makes streaming backups straightforward, but only if you bother to set it up.
Option 5: Plain SQLite
Worth flagging because it's often the right answer and people overlook it. If you're embedding a database in an application (a desktop app, a Tauri app, a CLI tool, a backend that runs on a single server), plain SQLite is fine. You don't need libSQL. SQLite is the most-deployed database in the world and the embedded use case is exactly what it was designed for.
The reasons to reach for libSQL over plain SQLite are specific:
- You need network access from multiple clients.
- You need replication.
- You want vector search.
- You want server-side migrations applied during deploys.
If none of those apply, plain SQLite with better-sqlite3 (Node) or sqlite3 (Python) or your language's equivalent is simpler and faster.
Layerbase Cloud also hosts plain SQLite (behind the same PG-wire trick we use for DuckDB) if you want hosted SQLite without the libSQL layer. It's a niche use case but it exists.
Option 6: Cloudflare D1
D1 is Cloudflare's SQLite-based database that runs on their Workers platform. Different sweet spot than Turso or Layerbase: it's designed for low-latency reads from edge functions, not as a general-purpose hosted SQLite. If your application is a Cloudflare Worker, D1 is great. If your application runs anywhere else, D1 is awkward because you can only reach it through a Worker.
The pricing model is friendlier than Turso's row-read meter (Cloudflare meters reads in larger units), but the lock-in to the Workers platform is real. Pick D1 if you're already on Workers. Pick something else otherwise.
If you're weighing D1 specifically, Cloudflare D1 alternatives does the same exercise from that side: what the Workers coupling actually costs you, and why getting data back out is the part to plan for.
If you're on D1 today and leaving, migrating from Cloudflare D1 to Layerbase walks the whole path into managed libSQL, and layerbase.com/migrate/cloudflare-d1 does it for you. One thing to know before you reach for wrangler d1 export: the dump rounds any integer past 2^53, so 64-bit ids don't survive it.
FAQ
Why is my Turso bill higher than I expected?
Almost always because Turso meters rows read, not requests, and a query that scans a table to satisfy a WHERE clause bills every row it scanned. An unindexed lookup that returns one row can count thousands of reads. This is the single most common surprise people report, and the fix on Turso is to add an index for every query pattern rather than to reason about request counts.
Does Turso charge per query or per row?
Per row. Turso's pricing meters storage and rows read, where a row read is every row your queries scan, including rows scanned and then filtered out. That is why analytics-shaped queries and COUNT(*) over a date range cost far more than their request count suggests. The free plan includes 500 million rows read and 10 million rows written per month with 5 GB of storage across up to 100 databases, and paid-plan overage on reads is billed per billion rows.
Turso free-plan allowances verified 2026-08-25 from turso.tech/pricing.
Can I move off Turso without changing my application code?
Yes, in the common case. The @libsql/client package is the standard libSQL client and it talks to Turso, to a local libSQL server, and to Layerbase Cloud without a code change: you swap the URL and the auth token. What does not carry over automatically is anything built on Turso-specific infrastructure, embedded replicas most of all, since no other host offers that model.
Do I actually need libSQL, or is plain SQLite enough?
Plain SQLite is enough more often than people assume. You reach for libSQL when you need network access from multiple clients, replication, vector search, or server-side migrations applied during deploys. If none of those describe your application, better-sqlite3 or your language's equivalent is simpler and faster than anything with a socket in front of it.
Is Cloudflare D1 a good Turso alternative?
Only if your application already runs on Cloudflare Workers. D1's pricing model is friendlier than Turso's row-read meter because Cloudflare meters reads in larger units, but you can only reach a D1 database through a Worker, so everything else in your stack needs an adapter you write and operate. Pick D1 if you are on Workers, and something else if you are not.
Which one to pick
| Option | What it is | When to pick it |
|---|---|---|
| Layerbase Cloud | Managed libSQL, flat per-instance pricing, no row-read meter | You want hosted libSQL and do not need geo-distributed replicas |
| The Layerbase CLI | A real libSQL server on your own machine | Local development against the same client code |
| Layerbase Desktop | The same local instances behind a GUI | You want a libSQL handy without tracking ports and auth tokens |
| Turso | The original libSQL host, embedded replicas at the edge | You need geo-distributed replicas and have audited your queries |
Self-hosted sqld | The open source libSQL server on a VM you own | A small workload, and you will actually configure TLS and backups |
| Plain SQLite | The embedded library, no network layer at all | You are embedding a database in one application on one machine |
| Cloudflare D1 | SQLite inside Cloudflare's Workers runtime | Your application is already a Worker |
The libSQL project itself is in great shape. The fork has aged well, the server (sqld) is solid, and the client libraries are mature. The thing that often goes wrong is the hosting decision, where people pick Turso without auditing query patterns and get surprised by row-read bills. If that's you, Layerbase Cloud and the other options above are worth a look.
For more on the engine itself, getting started with libSQL walks through the protocol, replication model, and TypeScript client with a real example.
Keep reading
- Your SQLite File Deserves a URLA SQLite file stops working the moment something other than one process needs it. Here is how to put the same file behind an address, with a Postgres driver or the libSQL client, without rewriting a schema.
- Cloudflare D1 alternatives: what you are actually coupled toD1 is SQLite, so your SQL is portable. The coupling is somewhere else: there is no wire protocol, the SQL export rounds any integer past 2^53, and you cannot get the file underneath. Here is an honest inventory, the alternatives, and when staying on D1 is the right call.
- Migrating from Turso to LayerbaseTurso meters reads and writes per row, which gets unpredictable on real workloads. Here is how to move your libSQL database to flat-priced managed hosting on Layerbase: dump it, load it, and point the same libSQL client at a new URL.
- Embedded Databases: SQLite, LibSQL, and DuckDBA practical guide to choosing between SQLite, LibSQL, and DuckDB when your project does not need a standalone database server.