There is a kind of frustration that comes when you realize you are building infrastructure for your infrastructure. I ran into this while working on a habit tracking app. The app used firestore as its backend. when there is a write request, it flags as dirty, There was a background process running specifically to flush the queue, clear the flags and confirm that it has written in the DB. It was working initially. But I realized I had spent three days building this, The actual habit features were not done, well After a few tests, I found out that reads were returning stale data, few user entries showing up twice and a few entries not showing up at all. At this point I was not working on habit tracking anymore, but more like debugging the system I had built to support it.

That is it, I started looking for a different approach, which eventually led me to test Turso's embedded replica and understood, what it actually replaces in a dev's workflow.

Why SQLite Seemed Like the Answer

SQLite was the first thing I considered. We don't need any network calls and connectivity checks to read and write data since the DB as a local file living on the Device, And offline first was my preference for the habit tracker, The Setup is zero configuration.

Then the obvious problem I got to know about is that, The SQLite file lives on one machine, and if we want our data on a second device the data won't be there, Deploying to a serverless function where there is no filesystem to put the file on, And do I want a backup?, Copying the file manually and hope nothing writes to the file while you are copying.

We can only use SQLite on one machine and the data is trapped in SQLite and getting it anywhere else was entirely my responsibility — that is just replacing one problem with other.

Do we want to Implement sync for SQLite?

Before talking about any tool, it is worth pointing what sync infrastructure actually involves when you build it yourself.

You need to track which records changed locally then flag them as unsynced, once that is done, Watch for a connectivity signal until the connection restores, Flush the unsynced records to the remote when connection restores. Handling what happens if the flush fails halfway through, what if the app closes mid-flush, what if two devices wrote 2 different or same values for a record while both were offline and what happens if the remote rejects a record because it already has a newer version.

Thinking about and solving all of it feels way too complex and remember I was building a habit tracker!. Few teams might get it right after a weeks work and Some teams may even ship apps with data loss and bugs, the record looks synced, but a conflict resolution edge case might have dropped the write, We don't know.

How Does Turso Solve the Sync Problem

Turso is a cloud database built on libSQL which is a fork of SQLite. SQLite has no network layer by design because It's just a C library that opens a file. libSQL adds HTTP access, replication and embedded replicas on top of SQLite's query engine.

This database accepts the same queries all along working with the same tooling uses SQL syntax in SQLite, But does all of this living on a cloud, And replicate data to wherever your application runs. You can also easily use it through their CLI, creating databases, getting connection URLs and auth tokens just by pointing your application to it.

Turso's architecture has one main primary database and replicas all over world. The primary DB is the main source where all writes go. Replicas are just copies of the primary DB sitting in other regions who only handle reads. So if there are users in Tokyo and London who wants to read, the Tokyo users reads from a Tokyo replica, and the London user reads from a London replica, For every query neither of them is making a round trip to the primary Database region. If the primary Database goes down the replicas can still serve reads. we might get data that which is a few seconds old but it will not get corrupted or just inconsistent data. This singlehandedly makes Turso globally fast, the data is already near the users before any read request even happens.

Diagram showing Turso's primary and replica architecture with global distribution across regions including Tokyo, London, and Mumbai
Figure 1 — Turso's primary and replica architecture
Turso Cloud analytics dashboard screenshot showing database usage metrics, read and write query counts, and storage consumption
Figure 2 — Turso Cloud analytics dashboard screenshot

How Turso Embedded Replica Works

This is the feature that is interesting so far that solves the biggest problem of sync directly.

The Turso setup is very straightforward remote connection, Our application sends queries over the network to Turso's servers and results come back. When I ran this from Hyderabad to a Primary Turso database on AWS Mumbai, the read latency averaged around 380ms per query, depends on the geography, Turso also supports Encryption at rest, WebAssembly triggers, Shared schema across multiple databases, Vector search for AI applications, Database per user architecture this one has been pulling a lot of users.

Embedded replica changes the model completely. It doesn't query the database directly with the app but keeps a local SQLite file on our own machine, that SQLite file stays in sync with the primary database or the replica, So all the reads are always from the local SQLite file but not from the Database directly, If we write something it goes to the remote primary Database and The sync between them when either you set a timer or manually tell it to sync.

The configuration change that activates this is two parameters added to the client initialization:

const client = createClient({
  url: 'file:backend/db/replica.db',
  syncUrl: process.env.TURSO_DATABASE_URL,
  authToken: process.env.TURSO_AUTH_TOKEN,
  syncInterval: 60,
});

After this change when the server started, four files appeared automatically on disk — replica.db, a WAL journal, a shared memory file, and a sync metadata file. I didn't need any additional setup to create a local copy of the SQLite file. The same read that took 380ms now came back in 0.52ms because it was hitting the local file, not the server in Mumbai. You can find the full implementation details in the embedded replica documentation.

PowerShell terminal showing Turso API response with write latency 640ms and sync latency 200ms
Figure 3 — Write to Turso remote returned in 640.85ms — the network cost to Mumbai. Manual sync pulled 233KB of data in 200.31ms. Reads after sync: 3.33ms.

The sync operation — calling client.sync() to pull the latest changes from the remote into the local file — took 200ms in my test. That 200ms is a one-time cost per sync cycle and not a per-query cost. After sync it's smooth — all the reads are free from the local file until the next sync is done.

What This Replaced

This replaced the dirty flag system and the entire background complex process, The flush queue, The retry logic and The conflict handling between devices. I didn't write any of it, All of that infrastructure already exists inside libSQL's sync engine.

The entire application can be synced with one function call which is client.sync(), we also call the same function when we want to pull fresh data from the remote into local replica and everything else is handled internally such as WAL frame tracking, retry on failures and consistency. Now this is a goldmine for solo devs or small teams. The latency number going from 380ms to 0.52ms matters and is pretty good but the real value is the sync engine we got from libSQL, And the time and energy I saved not having to worry about building and maintaining this whole complex part is just bananas.

Where It Does Not Help

While reads are completely offline and always come from the local file on your machine, but when there is a write it goes straight to the remote primary database in Mumbai, From my test a paragraph of 1200 words took 640ms from Hyderabad to Mumbai, So if your application needs very good write latency, embedded replica won't be useful.

If an offline user writes, the write fails then nothing saves anywhere, But there is an offline: true config option that makes it work when offline, after this config writes save to the local SQLite file first and gets synced to the cloud when internet's back. For habit tracker it works fine and good — just write it offline you can read it back from the local SQLite file on the machine, It automatically syncs once the internet is back, but for an app like Google Docs it won't work at all.

Turso has made one thing clear that they use the last write wins approach, means if two devices write the same record while both are offline, Whoever gets back online and sends the last request, and is latest to the primary, Survives. There is no automatic conflict resolution from Turso for 2 devices. If you are building something multi-user and collaborative, this is an edge case you should consider.

If your app is doing heavy concurrent writes or needs real time multiuser editing, Turso embedded replica is not the right tool for that case, but if you are in a local network like Mumbai, the write latency will be impressive.

How It Compares to What Else Is Out There

When I was checking out what can really help me out, My first thing was how do companies like WhatsApp scale to this large level and what are they using, I found out SQLite and decided to build around SQLite for scalability, also encryption was a must because people write their journals, I also came across Neon, it's serverless too but on Postgres, but it was not solving my offline first problem, and still slow because of round trip on every query, It wasn't fit for me. Then considering Supabase was good, but Supabase is a whole backend platform, if I was starting to build my app and if I was in my early stage, Supabase would have made sense but I only needed the database layer, and bringing in Supabase for that alone was not making sense to me.

PlanetScale is also MySQL-compatible but they removed their free tier way back in 2024 and they don't have an embedded replica model. For SQLite-native workloads my problems were not getting solved. Cloudflare D1 is compatible with SQLite and fast, but it only works in the Cloudflare Workers ecosystem, my app was not on Cloudflare.

All of them were great but, None of them had the specific needs that I had — SQLite syntax, offline-first reads, cloud sync, serverless and me not building sync.

When It Makes Sense to Reach For It

If you are someone who is building something where users need to read and write data offline first and don't want to build an entire sync system by yourself in the initial stage and save some time of your project, Turso nails where SQLite can't. But if you are looking for high scalability with real time collaboration and very heavy writes it's safe to say you need full Postgres features, This is not the right direction. Also building the sync yourself would be helpful because it costs between $24–$400 a month so you won't be spending on that for your lifetime, you will have more control on everything.

Building a whole sync engine for an offline-first app, It's totally worth checking out Turso embedded replica documentation and see if you are building something and if it exists already, it's a no-brainer opting for it.