Migrating from Turso to Layerbase
Turso made libSQL easy to host: a managed SQLite-compatible database you reach over the network, with a generous free tier. The catch shows up on real workloads, because the meter runs per row read and per row written. A busy app, an analytics query that scans a table, or a write-heavy job (a trading bot logging ticks, say) burns through the monthly allowance faster than you would expect, and the usual fix is to jump into a higher-limit tier rather than a flat price you can forecast.
If you would rather pay a flat per-instance price for the same database, libSQL on Layerbase Cloud is a direct swap. It is the same libSQL server (sqld) under the hood, so your schema, your SQL, and your @libsql/client code all come along. The only real work is copying your data and pointing the client at a new URL.
Contents
- What Actually Changes
- Set Up libSQL Locally with SpinDB
- Copy Your Data
- Point Your App at Layerbase
- What to Test
- The Managed Path: Layerbase Cloud
What Actually Changes
libSQL is libSQL on both ends, so most of your stack is untouched. Two things differ:
- The pricing model. Turso meters per row read and written (with fixed-plan options on top). Layerbase libSQL is flat per instance: the bill is the same whether you read a thousand rows a month or a billion. For anything past a hobby workload, this is the whole reason to move.
- The connection. You point
@libsql/clientat a new URL and auth token. That is usually a one-line env change.
What does not change: your tables, indexes, SQL, transactions, and the SQLite semantics your app relies on. The client library stays the same.
One honest note for write-heavy workloads: a flat instance has a fixed memory and CPU budget instead of a request meter. The difference is that you can see it and size it. Layerbase warns you as a database approaches its memory limit and lets you move up a tier or add headroom, rather than capping you at an opaque request count. If you are running something write-intensive, start on a plan with real headroom (Pro) rather than the entry tier.
Set Up libSQL Locally with SpinDB
Stand up libSQL locally first so you can load your dump and verify it before touching anything in production. SpinDB runs it with one CLI, no Docker. (What is SpinDB?)
Install SpinDB:
npm i -g spindb # npm
pnpm add -g spindb # pnpmCreate a libSQL instance and start it:
spindb create turso-migration -e libsql --startGet its URL:
spindb url turso-migrationhttp://127.0.0.1:8080You now have a local libSQL server to load your data into and test against.
Copy Your Data
libSQL speaks SQLite, so the cleanest path is a SQL dump. Export your Turso database to a .sql file:
turso db shell your-database ".dump" > turso-dump.sqlThat file is plain SQL: CREATE TABLE statements followed by INSERTs. As a sanity check, a tiny schema looks like this:
CREATE TABLE trades (
id INTEGER PRIMARY KEY,
symbol TEXT NOT NULL,
side TEXT NOT NULL,
price REAL NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
INSERT INTO trades (symbol, side, price, created_at)
VALUES ('BTC-USD', 'buy', 64250.0, '2026-06-28T17:00:00Z');Load the dump into your local SpinDB instance to confirm it restores cleanly:
spindb shell turso-migration < turso-dump.sqlVerify the row counts match what Turso reported:
SELECT count(*) FROM trades;count(*)
--------
1Once the local copy checks out, you load the same dump into your Layerbase database (next section).
Point Your App at Layerbase
Create a libSQL database on Layerbase Cloud. The dashboard gives you a connection URL (an https://...cloud.layerbase.dev host) and an auth token. Load your dump into it the same way, then change your app's connection.
If your code looks like this on Turso:
import { createClient } from '@libsql/client'
const client = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
})The only change is the two environment variables:
# Before (Turso)
TURSO_DATABASE_URL=libsql://your-db.turso.io
TURSO_AUTH_TOKEN=...
# After (Layerbase)
LIBSQL_DATABASE_URL=https://your-db.cloud.layerbase.dev
LIBSQL_AUTH_TOKEN=...const client = createClient({
url: process.env.LIBSQL_DATABASE_URL!,
authToken: process.env.LIBSQL_AUTH_TOKEN!,
})Your queries, transactions, and migrations are unchanged. It is the same client talking to the same kind of server.
What to Test
Before you cut over, confirm:
- Row counts match for every table, between Turso and the new database.
- Your hot queries run and return the same results.
- Write throughput holds under your real load. This is the one to watch if Turso's limits were your reason for leaving. Run your actual write pattern (the bulk insert, the tight logging loop) and confirm latency stays flat. If you see memory pressure, Layerbase tells you, and you size up a tier instead of hitting a request wall.
- Auth and connection limits behave the way your app expects from the new URL.
The Managed Path: Layerbase Cloud
Spin up managed libSQL on Layerbase Cloud and you get a flat per-instance price with no per-row meter, daily backups, and your choice of scale-to-zero (it sleeps when idle and wakes on the next connection) or always-on (pinned, never sleeps). The same libSQL you already build against, priced so a busy month does not surprise you.
If you want to prototype locally first, SpinDB runs the identical engine on your machine, so you can develop against a local libSQL and deploy the same schema to the cloud when you are ready.