Skip to content

Turso alternatives: libSQL hosting without row-read limits

10 min readlibSQLSQLiteDatabases

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

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 on email. 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.

bash
curl https://<your-host>.cloud.layerbase.dev/health

libSQL 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:

typescript
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?)

bash
npm i -g layerbase        # npm
pnpm add -g layerbase     # pnpm

Create an instance:

bash
lbase create libsql1 -e libsql --start

Get the connection URL:

bash
lbase url libsql1
text
http://127.0.0.1:8080

That'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.

bash
lbase stop libsql1
lbase start libsql1
lbase list

A 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:

bash
sqld --http-listen-addr 0.0.0.0:8080 --grpc-listen-addr 0.0.0.0:5001

You'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

OptionWhat it isWhen to pick it
Layerbase CloudManaged libSQL, flat per-instance pricing, no row-read meterYou want hosted libSQL and do not need geo-distributed replicas
The Layerbase CLIA real libSQL server on your own machineLocal development against the same client code
Layerbase DesktopThe same local instances behind a GUIYou want a libSQL handy without tracking ports and auth tokens
TursoThe original libSQL host, embedded replicas at the edgeYou need geo-distributed replicas and have audited your queries
Self-hosted sqldThe open source libSQL server on a VM you ownA small workload, and you will actually configure TLS and backups
Plain SQLiteThe embedded library, no network layer at allYou are embedding a database in one application on one machine
Cloudflare D1SQLite inside Cloudflare's Workers runtimeYour 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.