← Back to blog
Admin 13 min read

SQLite Can Handle Your Website. Yes, Yours.

Opinion

SQLite Can Handle Your Website. Yes, Yours.

Developers reach for PostgreSQL by reflex. But for blogs, e-commerce, SaaS dashboards, and the vast majority of web apps — SQLite does the job better, faster, and with zero infrastructure.

TL;DR — Do you need PostgreSQL?

SQLite Blogs, e-commerce, SaaS dashboards, APIs, contact forms, analytics. No server, $0/mo, faster reads. The right default for most websites.
PostgreSQL Heavy concurrent writes, multi-server architectures, PostGIS, full-text search with custom dictionaries, strict compliance requirements.
SQLite at the edge Use Cloudflare D1 for globally distributed SQLite. Manage it visually with MyD1.

Ask a developer what database to use for a new project. The answer is almost always PostgreSQL. It's the safe answer. The "serious" answer. The answer that sounds professional in a job interview.

But here's the uncomfortable truth: in our experience, most websites don't need PostgreSQL. They don't need a running server process, connection pooling, user management, replication configs, or a $15–50/month managed instance. They need a place to store rows and get them back quickly.

That's exactly what SQLite does. And it does it better than PostgreSQL for the vast majority of web workloads.

1 trillion+ Active SQLite databases worldwide
0 Servers to manage
$0 Monthly hosting cost

Why SQLite Is So Unpopular (And Why That's Wrong)

SQLite has a reputation problem. It's seen as a "toy database" — fine for mobile apps and prototypes, but not for "real" production workloads. This perception comes from four myths that refuse to die.

Myth "SQLite can't handle concurrent users"

This is the #1 objection. "What if two users write at the same time?" Developers picture data corruption and lost writes.

Reality In WAL mode (one line of config), SQLite handles unlimited concurrent readers and serialized writes. A single write takes microseconds. Unless you're doing thousands of concurrent writes per second — like a stock exchange or a real-time multiplayer game — you won't hit this limit. A blog with 10,000 daily visitors? A shop with 500 orders/day? SQLite doesn't even break a sweat.
Myth "SQLite doesn't scale"

People assume SQLite falls apart with "large" datasets. They picture a database struggling with 100K rows.

Reality SQLite handles databases up to 281 terabytes. Expensify runs their entire financial platform on SQLite at 4M queries/second. Pieter Levels (Nomad List, Remote OK) runs multiple profitable businesses on SQLite. The SQLite team themselves test with billions of rows. Your 50,000-product catalog is a rounding error. With proper indexes and configuration, indexed lookups on million-row tables typically return in under 1ms in our benchmarks.
Myth "SQLite isn't for production"

This one comes from the early 2000s, when web frameworks only supported MySQL and PostgreSQL. SQLite was "that embedded thing for Android."

Reality SQLite is the most deployed database engine in the world. It's in every iPhone, every Android phone, every Mac, every Windows PC, every Chrome browser, every Firefox browser. Apple, Google, Airbnb, and Facebook all use it in production. Cloudflare built an entire database product (D1) on top of it. If SQLite wasn't production-ready, your phone wouldn't work.
Myth "You can't use SQLite on a web server"

The old argument: SQLite is a file-based database, so it can't work across multiple server instances or in a distributed architecture.

Reality For a single-server deployment (which is what most websites are), SQLite on the same machine is the fastest possible option — zero network latency, zero connection overhead. And with Cloudflare D1, SQLite runs at the edge across 335+ locations. The "single file on one server" limitation disappeared. D1 gives you globally distributed SQLite.

The Real Reason PostgreSQL Wins by Default

It's not technical. It's cultural.

  • Tutorials overwhelmingly use PostgreSQL. Most "build a full-stack app" tutorials reach for Postgres. Developers learn it first and never question it.
  • Job postings ask for PostgreSQL. Saying "I use SQLite in production" sounds junior. Saying "I use PostgreSQL" sounds enterprise.
  • Resume-driven development. Engineers pick technologies that look good on a CV, not technologies that solve the problem simplest.
  • Herd mentality. "Netflix uses PostgreSQL, so I should too." Netflix also has over 300 million subscribers, thousands of engineers, and sub-millisecond latency requirements across dozens of regions. You have a landing page and a contact form.
  • Fear of looking unsophisticated. PostgreSQL feels "professional." SQLite feels "basic." But simple is not the same as simplistic. Choosing the right tool for the job is the most professional thing you can do.
PostgreSQL is a great database

This is not a hit piece on PostgreSQL. It's a world-class database that excels at heavy write loads, complex queries, full-text search, geospatial data, JSON operations, and multi-server architectures. The point isn't that PostgreSQL is bad — it's that most websites don't need what PostgreSQL offers, in our view. You're paying for a Formula 1 car to drive to the grocery store.

8 Real Use Cases Where SQLite Is All You Need

Let's get concrete. Here are everyday web applications that run perfectly on SQLite — with the actual schema to prove it.

1. Blog / Content Site SQLite is perfect

A blog is 99% reads, 1% writes. SQLite's strongest workload. No concurrent write pressure. Pages load from a single file — no network hop to a database server.

CREATE TABLE posts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  slug TEXT UNIQUE NOT NULL,
  title TEXT NOT NULL,
  content TEXT NOT NULL,
  published INTEGER DEFAULT 0,
  created_at TEXT DEFAULT (datetime('now')),
  updated_at TEXT DEFAULT (datetime('now'))
);

CREATE TABLE tags (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT UNIQUE NOT NULL
);

CREATE TABLE post_tags (
  post_id INTEGER REFERENCES posts(id),
  tag_id INTEGER REFERENCES tags(id),
  PRIMARY KEY (post_id, tag_id)
);

CREATE INDEX idx_posts_slug ON posts(slug);
CREATE INDEX idx_posts_published ON posts(published, created_at DESC);

Why PostgreSQL is overkill: You'd need a running Postgres server, connection pooling (PgBouncer), a managed instance ($15+/month), and monitoring — all to serve articles that change once a day. SQLite does it from a single file, for free, with faster reads.

2. Comment System SQLite is perfect

Comments are a classic "people think they need Postgres for this" workload. Even a popular blog gets maybe 50–200 comments per day. That's one write every 7 minutes. SQLite handles thousands of writes per second.

CREATE TABLE comments (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  post_id INTEGER NOT NULL REFERENCES posts(id),
  parent_id INTEGER REFERENCES comments(id),  -- threaded replies
  author_name TEXT NOT NULL,
  author_email TEXT,
  body TEXT NOT NULL,
  approved INTEGER DEFAULT 0,
  created_at TEXT DEFAULT (datetime('now'))
);

CREATE INDEX idx_comments_post ON comments(post_id, approved, created_at);
CREATE INDEX idx_comments_parent ON comments(parent_id);

Fetching all approved comments for a post:

SELECT id, parent_id, author_name, body, created_at
FROM comments
WHERE post_id = ? AND approved = 1
ORDER BY created_at ASC;

On a 100,000-comment table with proper indexes, this typically returns in under 1mssee our SQLite performance guide for benchmarking tips.

3. E-Commerce Store SQLite is perfect

This one surprises people. "An online store on SQLite? That can't be safe." But think about the actual workload: product catalog reads (fast), cart updates (a few writes per user session), order placement (one write per checkout). That's nothing.

CREATE TABLE products (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL,
  slug TEXT UNIQUE NOT NULL,
  description TEXT,
  price_cents INTEGER NOT NULL,
  stock INTEGER DEFAULT 0,
  category_id INTEGER REFERENCES categories(id),
  active INTEGER DEFAULT 1,
  created_at TEXT DEFAULT (datetime('now'))
);

CREATE TABLE orders (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id INTEGER REFERENCES users(id),
  status TEXT DEFAULT 'pending',  -- pending, paid, shipped, delivered
  total_cents INTEGER NOT NULL,
  shipping_address TEXT,
  created_at TEXT DEFAULT (datetime('now'))
);

CREATE TABLE order_items (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  order_id INTEGER NOT NULL REFERENCES orders(id),
  product_id INTEGER NOT NULL REFERENCES products(id),
  quantity INTEGER NOT NULL,
  price_cents INTEGER NOT NULL  -- price at time of purchase
);

CREATE INDEX idx_products_category ON products(category_id, active);
CREATE INDEX idx_products_slug ON products(slug);
CREATE INDEX idx_orders_user ON orders(user_id, created_at DESC);
CREATE INDEX idx_order_items_order ON order_items(order_id);

A store with 10,000 products and 200 orders/day? SQLite handles that without breaking a sweat. Shopify powers millions of merchants worldwide — but your store isn't Shopify. It's one store. One database. One file.

4. User Authentication & Accounts SQLite is perfect

User tables, sessions, password resets, email verification tokens — all classic CRUD operations with minimal write pressure.

CREATE TABLE users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  email TEXT UNIQUE NOT NULL,
  name TEXT,
  password_hash TEXT NOT NULL,
  email_verified INTEGER DEFAULT 0,
  created_at TEXT DEFAULT (datetime('now'))
);

CREATE TABLE sessions (
  id TEXT PRIMARY KEY,  -- session token
  user_id INTEGER NOT NULL REFERENCES users(id),
  expires_at TEXT NOT NULL
);

CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_sessions_user ON sessions(user_id);
CREATE INDEX idx_sessions_expires ON sessions(expires_at);

Even with 100,000 registered users and 5,000 daily active sessions, the database stays under 50MB. SQLite loads that entirely into memory and serves every query in microseconds.

5. SaaS Dashboard / Admin Panel SQLite is perfect

Internal dashboards, admin panels, project management tools, CRMs — these are typically used by a small team (1–50 people). The data volumes are modest, the query patterns are simple, and uptime matters more than raw throughput.

CREATE TABLE projects (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL,
  owner_id INTEGER REFERENCES users(id),
  status TEXT DEFAULT 'active',
  created_at TEXT DEFAULT (datetime('now'))
);

CREATE TABLE tasks (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  project_id INTEGER NOT NULL REFERENCES projects(id),
  title TEXT NOT NULL,
  description TEXT,
  assignee_id INTEGER REFERENCES users(id),
  status TEXT DEFAULT 'todo',  -- todo, in_progress, done
  priority INTEGER DEFAULT 0,
  due_date TEXT,
  created_at TEXT DEFAULT (datetime('now'))
);

CREATE INDEX idx_tasks_project ON tasks(project_id, status);
CREATE INDEX idx_tasks_assignee ON tasks(assignee_id, status);

SQLite's advantage here: zero operational overhead. No database server to monitor, patch, or restart. Back up the entire database by copying one file. Restore by copying it back.

6. Contact Forms, Waitlists & Newsletters SQLite is perfect

Landing pages with a "Join the waitlist" form. Contact forms. Newsletter signups. These are append-only workloads — the simplest possible database pattern.

CREATE TABLE waitlist (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  email TEXT UNIQUE NOT NULL,
  name TEXT,
  source TEXT,  -- where they signed up from
  created_at TEXT DEFAULT (datetime('now'))
);

CREATE TABLE contact_messages (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL,
  email TEXT NOT NULL,
  message TEXT NOT NULL,
  read INTEGER DEFAULT 0,
  created_at TEXT DEFAULT (datetime('now'))
);

You do not need a $25/month Supabase instance for a waitlist. One SQLite file. Done.

7. Lightweight Analytics / Event Tracking SQLite is perfect

Page views, button clicks, feature usage tracking. For small-to-medium sites, this is a write-heavy but low-volume workload — perfect for SQLite with WAL mode and batched inserts.

CREATE TABLE events (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  event_type TEXT NOT NULL,
  page TEXT,
  referrer TEXT,
  user_agent TEXT,
  ip_hash TEXT,  -- hashed for privacy
  created_at TEXT DEFAULT (datetime('now'))
);

CREATE INDEX idx_events_type_date ON events(event_type, created_at);
CREATE INDEX idx_events_page ON events(page, created_at);

Query your daily page views:

SELECT page, COUNT(*) as views
FROM events
WHERE event_type = 'pageview'
  AND created_at >= date('now', '-7 days')
GROUP BY page
ORDER BY views DESC
LIMIT 20;

With proper indexes, this aggregation runs in milliseconds — even on millions of rows.

8. REST / JSON API Backend SQLite is perfect

API backends for mobile apps, SPAs, or third-party integrations. If your API serves under 10,000 requests per minute (which covers the vast majority of apps), SQLite handles it trivially.

// SvelteKit API route with D1 (SQLite at the edge)
export async function GET({ url, platform }) {
  const db = platform.env.DB;
  const page = Number(url.searchParams.get('page') || 1);
  const limit = 20;
  const offset = (page - 1) * limit;

  const products = await db
    .prepare('SELECT id, name, slug, price_cents FROM products WHERE active = 1 ORDER BY created_at DESC LIMIT ? OFFSET ?')
    .bind(limit, offset)
    .all();

  return Response.json(products.results);
}

Zero connection pooling. Zero ORM configuration. Bind the database, write the query, return the result. See the full tutorial.

When You Actually Need PostgreSQL

SQLite isn't the right choice for everything. Here's when PostgreSQL (or MySQL) genuinely makes sense:

You need PostgreSQL when...Example
Heavy concurrent writes (1000+/second sustained)High-frequency trading, real-time bidding, IoT sensor ingestion — PostgreSQL's MVCC handles this natively
Multiple application servers writing to the same databaseHorizontally scaled API behind a load balancer — SQLite is designed for single-process access
Advanced SQL features you can't live withoutRecursive CTEs, window functions, stored procedures, materialized views
Specialized data typesPostGIS for geospatial, tsvector for full-text search, JSONB with GIN indexes
Strict compliance requirementsFinancial systems needing serializable isolation across distributed transactions

Notice the pattern? These are enterprise-scale or specialized workloads. Not blogs. Not online stores. Not SaaS dashboards with 200 users.

The Real Comparison

PostgreSQL
  • Requires a running server process
  • $15–50/month for managed hosting
  • Connection pooling required
  • Backups need pg_dump or WAL archiving
  • Needs user/role management
  • Network latency on every query
  • Great for heavy concurrent writes
SQLite
  • No server — it's a library
  • $0/month — it's a file
  • No connection overhead
  • Backup = copy one file
  • No user management needed
  • Zero network latency (same machine)
  • Great for read-heavy web apps

Who Uses SQLite in Production?

Expensify Financial platform. 4M queries/sec on a single server. SQLite.
Pieter Levels Nomad List, Remote OK, Photo AI. Multiple profitable businesses. All SQLite.
Apple Core Data, Messages, Photos, Safari. Every Apple device runs SQLite.
Cloudflare D1 — globally distributed SQLite at the edge. Powers thousands of production apps.
WhatsApp Local message storage for 2 billion users. All SQLite.
Airbnb Mobile app data. Offline-first architecture. SQLite.

Stop Overthinking Your Database Choice

You're building a website. It has users, some content, maybe a shopping cart. The database will hold a few hundred thousand rows. Maybe a million. Maybe ten million if you're lucky.

SQLite handles all of that. Without a server. Without a monthly bill. Without connection strings, user roles, replication lag, or 3 AM pager alerts because the database ran out of connections.

The best database is the one that disappears. The one you don't think about. The one that just works, silently, in the background, while you focus on building your product.

For the vast majority of websites, that database is SQLite.

SQLite at the edge with Cloudflare D1

Want SQLite's simplicity with global distribution? Cloudflare D1 runs SQLite across 335+ edge locations. Your data is close to every user, worldwide. And MyD1 lets you browse, query, and manage your D1 databases visually — no terminal needed. Download MyD1 free.

Related: 10 Ways to Make SQLite Blazing Fast · D1 vs MySQL vs PostgreSQL · Build a Full-Stack App on Cloudflare for Free · Why AWS Is So Slow