Application Development

SQLite Does Not Persist on Serverless Hosting: 5 Solutions

J
James Eriksson
··12 min read
SQLite fails on serverless due to ephemeral filesystems. Learn why data vanishes and explore 5 solutions: Turso, D1, Railway, Render, or Postgres.
TL;DR
  • Serverless filesystems are ephemeral: each invocation gets a fresh container, and your data vanishes when the container ends.
  • Claude Code, Cursor, and Lovable generate SQLite-backed apps by default; they work locally but fail silently on Vercel and Netlify.
  • Five solutions exist: accept data loss (demo phase), read-only SQLite (static data), Turso/D1 (edge-native), Railway/Render (persistent containers), or Postgres (high concurrency).
  • Turso has 24K GitHub stars and starts at $9/month; Railway and Render start at $7/month with persistent disks.
  • Ship offers flat-rate managed hosting with SQLite persistence for Lovable and other AI-generated apps.

SQLite does not persist on serverless hosting because serverless functions use ephemeral filesystems. Each invocation gets a fresh container with no access to previously written data. When the function ends, the filesystem is discarded and your data vanishes. This is why deploying apps to Vercel, Netlify, or AWS Lambda with local SQLite files results in silent data loss. If you built an app with Claude Code or Cursor and deployed it to serverless, this is almost certainly why your data disappeared.

Why SQLite Vanishes on Serverless: The Ephemeral Filesystem Problem

Serverless functions run in containers that start fresh with each invocation. The filesystem is temporary and destroyed when the function ends. SQLite writes to disk; if that disk doesn't persist, the database is lost. Ephemeral means "temporary" -- your serverless container is exactly that.

When you deploy to Vercel or AWS Lambda, the platform doesn't provide a persistent disk. You get a clean filesystem every time the function runs. There's a /tmp directory available, but it also vanishes after the invocation ends. SQLite assumes its data file will exist after the process exits. On serverless, that assumption is fundamentally broken.

Here's what happens step-by-step: Your app writes a user record to database.db. The function executes successfully and ends. The container vanishes. The platform destroys the filesystem. The next invocation starts a brand new container from scratch. Your app looks for database.db -- it doesn't exist. Depending on how you coded it, either you create an empty database (losing all prior data) or your code throws an error. Either way, the prior data is gone. No error logs explicitly say "your data was destroyed." It's silent. Your app doesn't crash. It just stops seeing data.

This failure mode is completely hidden during local development. Your laptop's filesystem persists. You run the app, write data to database.db, close it, run it again, and the data is still there. You assume the internet works the same way. It doesn't. This is the core of the problem.

AI-Generated Apps Made This Problem Urgent

Claude Code, Cursor, and Lovable generate full-stack web applications with Express or Flask backends and SQLite by default. These code generation tools don't account for serverless infrastructure constraints. You run the app locally (works perfectly), then deploy it to serverless (data vanishes immediately). This gap between local dev and serverless is new and urgent.

Ask Claude Code to "build a todo app" and it generates a complete Express server that uses SQLite to store todos. The code is well-written and correct. It runs flawlessly on your machine. You add todos, close the browser, reload the page, and they're still there. You're delighted. You push the code to GitHub, connect it to Vercel, deploy. A user adds their first todo. You check the app. It's there. You refresh the browser. It's gone. You check Vercel's logs. No errors reported. The database file simply vanished from Vercel's ephemeral filesystem.

This is happening to thousands of builders right now. Lovable, Claude Code, Cursor -- they all generate production code that assumes a persistent filesystem. The tools aren't broken; that assumption is reasonable on a traditional VPS or managed hosting platform. It's fatal on serverless. And the worst part is there's no error message. Your app doesn't crash. It functions perfectly. It just silently loses data. You might not notice for days or weeks if you're testing locally before every deploy.

Quick Diagnostic: Does Your App Actually Need to Persist Data?

If your app only reads static data bundled at deploy time, read-only SQLite works fine. If it never writes to the database, you can package a.db file as a build artifact and deploy it. Most production applications need to write data, so this is a temporary solution.

Think through your specific use case: Does your app write data dynamically, or does it only read from a static dataset? A documentation site or blog with a static SQLite database can bundle it and open it in read-only mode. A todo app that stores todos in the database needs real persistence. Read-only SQLite doesn't work for user-generated content, session data, preferences, or any dynamic state.

Some teams use a hybrid: read-only SQLite to distribute reference data (product catalogs, feature flags, settings that don't change within a deploy cycle) and a separate persistent database for writes. This works but adds architectural complexity.

Solution Ladder: From Workarounds to Production Persistence

Five distinct tiers exist, ranging from "accept data loss during development" to "managed enterprise SQL database." Each tier has tradeoffs.

Tier 1: Accept Data Loss (Demo / Early Dev)

For early-stage prototypes or investor demos that run for a few hours, you can simply accept that data will vanish. Deploy to Vercel or Netlify, let the ephemeral filesystem be what it is. Reset your database on every deploy or don't store anything important. This costs zero dollars per month and is perfectly reasonable for "show this to investors in 20 minutes" scenarios.

Tier 2: Read-Only SQLite (Static Reference Data)

Bundle your SQLite database as a build artifact. Open it in read-only mode. This works for reference data that doesn't change during the lifetime of a deployed app: product catalogs, feature configurations, settings, localization data. Deployment size matters (SQLite files can get large), but Vercel and Netlify support uploading large assets.

Tier 3: Edge-Native SQLite (Turso, Cloudflare D1)

Turso and Cloudflare D1 are managed SQLite databases specifically designed for serverless environments. Instead of file-based storage, you query data over HTTP APIs. Your data is replicated across a global network of edge nodes. You get persistence without depending on an ephemeral filesystem.

Tier 4: Persistent Containers (Railway, Render)

Railway and Render provide containers with attached persistent volumes. You get a real filesystem that survives across container restarts. SQLite works exactly as it does on a traditional server. You're no longer serverless (you don't get the per-invocation granularity of Lambda or Vercel functions), but you get simplicity and flat-rate billing.

Tier 5: Managed SQL (Vercel Postgres, Supabase, Neon)

Migrate your schema to Postgres or MySQL. You get true horizontal scalability, multi-writer concurrency, and years of mature ops tooling. This is necessary for apps with high concurrent write loads or advanced query requirements. Vercel Postgres starts at $15/month; similar pricing for Supabase.

Turso and Cloudflare D1: SQLite for Serverless

Turso is SQLite forked and rebuilt in Rust, specifically optimized for serverless deployments. Cloudflare D1 is Cloudflare's native managed SQLite offering. Both use HTTP APIs instead of file-based access. Both replicate data globally across edge nodes. Both offer predictable pricing.

Turso solves this exact problem by fundamentally rearchitecting SQLite for the serverless era. Instead of writing to a local disk, you send SQL queries over HTTPS to Turso's distributed edge nodes. Your data replicates across multiple regions. Latency is typically 50-100ms from anywhere on Earth.

Turso is production-ready. The GitHub repository has 24K stars. The free tier provides 500M rows. Paid plans start at $9/month and scale from there.

Cloudflare D1 takes a similar approach but integrates tightly with Cloudflare Workers. If you're already building on Workers, D1 is the natural choice. Pricing is based on row reads and writes; a simple application costs a few dollars per month.

The tradeoff: both introduce slightly higher latency than local file-based SQLite because every query crosses the network. You also can't use SQLite features that assume direct filesystem access (like ATTACH DATABASE). But for the vast majority of web applications, the simplicity and persistence win outweigh these costs.

Choose this approach when: You want SQLite's simplicity and ACID guarantees, you're building on serverless, and you don't require sub-millisecond latency. For read-heavy workloads, Turso is often faster than Vercel Postgres due to edge distribution.

Persistent Containers: Railway and Render as Middle Ground

Railway and Render provide containers with persistent volumes attached. You get a real filesystem that survives across container restarts and updates. SQLite works exactly as it does on a traditional Linux server. You're no longer serverless (no per-invocation scaling), but you gain simplicity and predictable billing.

Railway's model is straightforward: Spin up a container. Attach a persistent volume to it. Write your application code exactly as you would for a traditional server. Railway keeps the container running and the volume attached across restarts. Your data persists. No ephemeral filesystem. Pricing is flat: $7-$10 per month for a small container plus the cost of storage. No usage-based surprises.

Render offers the same architecture. Create a persistent disk, attach it to your container, and SQLite works natively. Render's free tier puts containers to sleep after 15 minutes of inactivity (your app cold-starts when traffic arrives). Paid plans start at $7/month for always-on containers.

For concurrent read workloads, enable SQLite's WAL (Write-Ahead Logging) mode at startup. This allows multiple readers while one writer is active:

db.execute("PRAGMA journal_mode = WAL;")
db.execute("PRAGMA synchronous = NORMAL;")

Backup your database using Litestream, an open-source replication tool. It continuously streams your SQLite database to S3 or Cloudflare R2. If your database becomes corrupted or your container dies, you can restore from a recent snapshot.

Choose this approach when: You want the simplicity of real SQLite without learning a new database system, your workload doesn't involve millions of concurrent writes, and you prefer predictable flat-rate pricing over usage-based billing.

Migrating Away: When to Move to Postgres

SQLite is not designed for high-concurrency, multi-writer workloads. It locks the entire database file when writing. While one writer is active, all readers are blocked. Run this on a production app with 50+ simultaneous users and SQLite becomes a bottleneck within weeks. Performance degrades predictably as concurrency increases.

Postgres is built for exactly this scenario. Multiple writers can work concurrently without blocking readers. It scales to billions of rows and thousands of queries per second. The database locks only the specific rows you're modifying, not the entire table.

When should you make this switch? If you're running on serverless platforms: Migrate to Vercel Postgres or Supabase (managed Postgres). If you're building multi-tenant applications: Separate databases per customer requires Postgres for safety and performance. If you're handling financial transactions: Postgres' ACID guarantees and concurrency control are critical. If you're expecting high traffic: When your SQLite bottleneck becomes obvious, you're already losing users.

Migration is straightforward if you use an ORM like Prisma, SQLAlchemy, or Sequelize. You change the connection string and run a schema migration. Your queries stay the same.

Cost: Vercel Postgres starts at $15/month for 1GB of storage. Supabase pricing is similar. For small applications, this costs less than paying yourself to debug SQLite locking issues in production.

Ship: Managed Persistence Without the Ops Load

Ship provides managed containers with persistent volumes, flat-rate monthly pricing, real SSH access, and automatic database backups. You get PaaS simplicity without the lock-in of Vercel or the per-function-invocation pricing of Lambda.

Your application runs in a real container with a persistent volume attached. SQLite works natively. Your data survives container restarts. There's no ephemeral filesystem. This solves the core problem: you can use SQLite exactly like you would on a traditional server.

Pricing is straightforward and predictable. Unlike Vercel (which charges per function invocation), Railway (which charges per container-hour), or Turso (which charges per query), Ship charges one flat monthly rate. If your traffic increases 100x, your bill stays the same. This is the opposite of usage-based pricing and appeals to builders who want predictability.

You get real SSH access. Need to inspect the database? SSH into the container and run SQLite commands directly. Need to run a migration or one-off query? SSH in and execute it. Vercel and Netlify don't allow SSH access; everything must go through their APIs. Ship gives you full control.

Backups happen automatically. Your database is continuously backed up to persistent storage. If something goes wrong, you can restore to an earlier point in time.

This directly answers the builder's dilemma: "I built my app with Claude Code, it's written for SQLite, and I just want it to work without spending a week learning databases." Ship makes that possible.

Deploy to Ship for managed container hosting with persistent SQLite out of the box. If you're hosting Lovable or other AI-generated apps, Ship handles database persistence natively.

Frequently Asked Questions

Why doesn't SQLite work on serverless platforms?

Serverless containers are destroyed after each invocation. The filesystem is ephemeral. SQLite writes to disk; once the container is gone, so is your data. This is fundamental to how serverless platforms work, not a bug.

Can I use SQLite with Vercel?

No. Vercel's serverless functions do not provide persistent local storage. Any SQLite database file written during a function invocation will be lost when the invocation ends.

What are the alternatives to SQLite on serverless?

Turso and Cloudflare D1 for managed SQLite with HTTP APIs. Vercel Postgres, Supabase, or Neon for managed Postgres. Or switch to Railway or Render, where traditional SQLite works natively on persistent containers.

Does SQLite work on Railway?

Yes. Railway provides persistent volumes that you can attach to containers. SQLite works exactly as it would on a traditional server, with full read and write support.

How do you back up SQLite on serverless?

If using a persistent container (Railway, Render), use Litestream to continuously replicate your database to S3 or Cloudflare R2. If using Turso or D1, the provider handles backups automatically.

Can you use SQLite as read-only on serverless?

Yes, if you bundle the.db file in your deployment and open it in read-only mode. This works for static reference data but not for applications that write user data.

What is libSQL and Turso?

libSQL is SQLite forked and rebuilt in Rust. Turso is a managed version of libSQL with edge distribution, global replication, and HTTP APIs. The GitHub repository shows libSQL with 5K+ stars.

The Bottom Line

SQLite fails on serverless because filesystems are ephemeral. This is a fundamental design constraint of serverless architecture, not a bug or limitation you can work around. AI-generated code generators make this problem urgent because tools like Claude Code default to SQLite + Express, and most builders don't know that serverless changes the rules.

You have five clear solutions at increasing levels of complexity: accept data loss for demos, use read-only SQLite for static data, move to Turso or D1 for edge-native persistence, use Railway or Render for persistent containers, or migrate to Postgres for high-concurrency workloads.

If you want to stay with SQLite without ops overhead, deploy to Ship for managed container hosting with built-in database persistence.

Deploy SQLite without ephemeral filesystems
Ship provides persistent container hosting with flat pricing, designed for apps built with Claude Code, Cursor, and Lovable.
Get Started Free

Ready to self-host your own apps?

One server. Multiple apps. No per-app fees.

Get started →
SQLite on Serverless: Why It Fails & 5 Solutions