Turso — облачная база данных на основе libSQL, открытого форка SQLite. Идея простая: взять SQLite, одну из самых простых и быстрых встраиваемых СУБД, и добавить то, чего ей не хватает для веба, — доступ по сети и HTTP, реплики рядом с приложением и удобное управление тысячами отдельных баз.
Параллельно компания развивает новый движок Turso — SQLite-совместимую СУБД, переписанную на Rust, с асинхронным вводом-выводом и работой над конкурентной записью. На момент обновления статьи статус этого проекта лучше проверять на сайте Turso: для продакшна обычно берут Turso Cloud на libSQL.
libSQL, Turso Cloud и новый Turso: что есть что
libSQL — open-source форк SQLite на C. Добавляет сетевой сервер (sqld) с HTTP-доступом, встроенные (embedded) реплики, шифрование и векторный поиск. Использует формат файлов SQLite и её SQL-диалект, плюс собственные расширения.
Turso Cloud — managed-сервис поверх libSQL. Базы создаются через дашборд, CLI или API. Тарифы, лимиты и доступные регионы со временем менялись, поэтому актуальные условия смотрите на сайте Turso.
Turso (Rust) — новое поколение движка, переписанное с нуля. Цель — совместимость с SQLite плюс асинхронность и конкурентная запись. Перед использованием в продакшне проверьте, вышел ли он из бета-статуса.
Зачем SQLite рядом с приложением
Классическая схема: приложение в одном регионе, PostgreSQL — в другом, и каждый запрос к базе платит сетевую задержку. Чем больше запросов на одну страницу, тем заметнее это ожидание.
Embedded replica в Turso — локальная копия базы в файле рядом с приложением. Чтение идёт из локального файла без сетевого запроса, запись уходит в основную базу в облаке, а реплика периодически синхронизируется.
Важно: embedded replica нужна файловая система. Она работает в Node.js-приложении на VPS, в контейнере, на Fly.io и т. п., но не в бессерверных edge-средах без диска (Cloudflare Workers, Vercel Edge) — там подключайтесь к Turso по HTTP.
// Embedded replica в Node.js-приложении на сервере
import { createClient } from '@libsql/client'
const client = createClient({
url: 'file:local.db', // локальная реплика
syncUrl: process.env.TURSO_URL, // основная база в Turso Cloud
authToken: process.env.TURSO_AUTH_TOKEN,
syncInterval: 60, // автосинхронизация раз в 60 секунд
})
// Чтение — из локального файла
const users = await client.execute({ sql: 'SELECT * FROM users WHERE id = ?', args: [userId] })
// Запись — в основную базу по сети
await client.execute({
sql: 'INSERT INTO users (name, email) VALUES (?, ?)',
args: [name, email],
})
Учтите, что реплика отстаёт от основной базы до следующей синхронизации — читать «только что записанное» с другого узла можно не сразу.
Настройка Turso
Создание базы и подключение
# Установка CLI
curl -sSfL https://get.tur.so/install.sh | bash
turso auth login
turso db create myapp
# URL и токен
turso db show myapp
turso db tokens create myapp
# .env (не коммитьте)
TURSO_URL=libsql://myapp-username.turso.io
TURSO_AUTH_TOKEN=eyJhbGci...
Node.js + Drizzle
npm install @libsql/client drizzle-orm
npm install -D drizzle-kit
// lib/db.ts
import { createClient } from '@libsql/client'
import { drizzle } from 'drizzle-orm/libsql'
import * as schema from './schema'
const client = createClient({
url: process.env.TURSO_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
})
export const db = drizzle(client, { schema })
// lib/schema.ts
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
import { sql } from 'drizzle-orm'
export const users = sqliteTable('users', {
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
name: text('name').notNull(),
email: text('email').notNull().unique(),
plan: text('plan', { enum: ['free', 'pro', 'enterprise'] }).default('free'),
createdAt: integer('created_at', { mode: 'timestamp' })
.default(sql`(unixepoch())`),
})
export const posts = sqliteTable('posts', {
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
title: text('title').notNull(),
content: text('content'),
authorId: text('author_id').references(() => users.id),
published: integer('published', { mode: 'boolean' }).default(false),
})
import { db } from '@/lib/db'
import { users, posts } from '@/lib/schema'
import { eq } from 'drizzle-orm'
const publishedPosts = await db
.select({ id: posts.id, title: posts.title, authorName: users.name })
.from(posts)
.leftJoin(users, eq(posts.authorId, users.id))
.where(eq(posts.published, true))
.all()
Сравнение Drizzle с Prisma — в статье Prisma vs Drizzle.
Мультитенантность: отдельная база на клиента
Создавать базы в Turso дёшево, поэтому популярен паттерн «одна база на тенанта»: у каждого клиента своя база, а не общая схема с row-level security.
// SaaS: база для нового тенанта
import { createClient as createTursoApi } from '@tursodatabase/api'
import { createClient } from '@libsql/client'
const turso = createTursoApi({
token: process.env.TURSO_API_TOKEN!,
org: process.env.TURSO_ORG!,
})
export async function createTenantDatabase(tenantId: string) {
const name = `tenant-${tenantId}`
const database = await turso.databases.create(name, { group: 'default' })
const token = await turso.databases.createToken(name)
const url = `libsql://${database.hostname}`
const client = createClient({ url, authToken: token.jwt })
await client.execute(`
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
created_at INTEGER DEFAULT (unixepoch())
)
`)
// Токен храните зашифрованным (masterDb — ваша служебная база)
await masterDb.execute({
sql: 'INSERT INTO tenants (id, db_url, db_token) VALUES (?, ?, ?)',
args: [tenantId, url, encrypt(token.jwt)],
})
return { url }
}
Плюсы подхода:
- данные клиентов физически разделены — ошибка в
WHEREне покажет чужие строки; - удалить данные клиента = удалить его базу;
- бэкап и восстановление можно делать по отдельному клиенту;
- клиенту можно выдать доступ только к его базе.
Минусы: миграции схемы нужно прогонять по всем базам, а сквозная аналитика по клиентам требует отдельного хранилища.
Сравнение Turso с аналогами
| Параметр | Turso | Cloudflare D1 | Neon | Supabase | |---|---|---|---|---| | Движок | libSQL (SQLite) | SQLite | PostgreSQL | PostgreSQL | | Доступ по HTTP | Да | Через Workers binding и API | Да (serverless driver) | Да (REST, клиент) | | Embedded replicas | Да | Нет | Нет | Нет | | Много отдельных баз | Основной сценарий | Возможно | Возможно (проекты/ветки) | Обычно одна база на проект | | Self-hosted вариант | libSQL server | Нет | Открытый код, сложный self-host | Да | | Совместимость | SQLite | SQLite | PostgreSQL | PostgreSQL |
Тарифы и лимиты всех сервисов меняются — сравнивайте по их сайтам на момент выбора. Про российские альтернативы — в статье аналоги Supabase в России.
Когда выбирать Turso
- Нужна SQLite-совместимость и привычные инструменты (Drizzle, libSQL-клиенты).
- Строите SaaS с базой на каждого клиента.
- Хотите читать данные из локальной реплики рядом с сервером приложения.
- Подключаетесь из edge-функций по HTTP.
Когда выбрать Neon или другой PostgreSQL
- Нужны возможности PostgreSQL: pgvector, PostGIS, RLS, богатые типы.
- Много конкурентной записи и сложных транзакций.
- Команда уже хорошо знает PostgreSQL.
Когда выбрать Cloudflare D1
- Проект целиком живёт в Cloudflare Workers.
- Устраивают лимиты D1 на размер базы и модель доступа через binding.
Turso в России и self-hosted libSQL
Turso — американская компания. Доступность сервиса и способы оплаты из России могут меняться, поэтому проверьте их сами до того, как строить на Turso продакшн. Независимый вариант — self-hosted libSQL server:
docker run -p 8080:8080 \
-e SQLD_NODE=primary \
ghcr.io/tursodatabase/libsql-server:latest
import { createClient } from '@libsql/client'
const client = createClient({
url: 'http://localhost:8080',
// для публичного доступа включите аутентификацию по JWT в sqld
})
Для self-hosted варианта бэкапы полностью на вас: том с данными sqld, расписание, копия вне сервера.
Бэкап баз Turso
Репликация в облаке защищает от сбоя железа, но не от DELETE без WHERE, неудачной миграции или удалённой базы: такие изменения реплицируются так же исправно. Поэтому нужны собственные копии.
#!/bin/bash
# Логический дамп базы Turso в SQL
set -euo pipefail
mkdir -p backups
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
turso db shell myapp .dump | gzip > "backups/turso_${TIMESTAMP}.sql.gz"
gzip -t "backups/turso_${TIMESTAMP}.sql.gz"
Дамп — это SQL в диалекте SQLite. Чтобы проверить его и заодно получить готовый файл базы, восстановите дамп в обычный SQLite:
gunzip -c backups/turso_20261006_030000.sql.gz | sqlite3 turso-copy.db
sqlite3 turso-copy.db "PRAGMA integrity_check;"
Если вы используете расширения libSQL (например, векторные индексы), обычный sqlite3 может не принять часть дампа — тогда храните SQL-дамп как есть. Подробнее о копиях SQLite — в статье как сделать бэкап SQLite, о восстановлении — как восстановить базу из бэкапа.
Получившийся SQLite-файл можно отправлять в dbsend как SQLite-базу: каждая загрузка станет версией с таблицами и числом строк, а на платных тарифах — с diff между версиями:
dbsend backup ./turso-copy.db -d "$DBSEND_DATABASE_ID" -e sqlite -l "turso-$(date +%F-%H%M)"
Если дамп в sqlite3 не загружается, сохраните сам SQL-файл как -e generic_file: dbsend будет хранить версии и проверять контрольную сумму, но без разбора содержимого.
Частые вопросы
Turso бесплатный?
У Turso Cloud есть бесплатный тариф с лимитами на число баз, объём хранилища и количество операций, и платные тарифы. Конкретные цифры менялись, поэтому смотрите страницу тарифов Turso. libSQL и self-hosted сервер — open source.
В чём разница libSQL и SQLite?
libSQL — форк SQLite с расширениями: сетевой сервер и HTTP-доступ, embedded-реплики, шифрование, векторный поиск. Базовый формат файлов и SQL совместимы с SQLite, но возможности форка обычный sqlite3 не понимает. Подробнее о самой SQLite — в статье что такое SQLite.
Работает ли Drizzle с Turso?
Да, у Drizzle есть адаптер drizzle-orm/libsql, миграции делаются через drizzle-kit. Prisma подключается к Turso через driver adapter для libSQL.
Как Turso решает проблему одного писателя в SQLite?
В Turso Cloud на libSQL все записи идут в основную базу, а реплики синхронизируются после — то есть писатель по-прежнему один. Конкурентную запись разрабатывают в новом движке на Rust; проверьте его текущий статус перед использованием.
Можно ли перенести данные из PostgreSQL в Turso?
Да, но SQLite поддерживает не все типы PostgreSQL: массивы, JSONB-индексы и специфичные функции придётся заменить. Схему обычно переписывают (SERIAL → INTEGER PRIMARY KEY, JSONB → TEXT с JSON-функциями), а данные переносят скриптом (выгрузка в CSV или INSERT-ы) в SQLite-файл, который затем загружают в Turso. Учтите, что pgloader здесь не поможет: он умеет загружать данные только в PostgreSQL.
Что такое группы баз данных в Turso?
Группа — набор баз с общими настройками размещения и версии; токены доступа можно выпускать на всю группу. Для SaaS с базой на клиента все тенантские базы обычно помещают в одну группу. Детали модели размещения зависят от текущего тарифа — сверяйтесь с документацией Turso.



