SQLite vs MySQL for a CMS: When the Small Database Wins

Count your writers and count your servers — the answer falls out of those two numbers

July 25, 2026 · 14 min read
SQLite vs MySQL for a CMS: When the Small Database Wins

Ask ten developers which database to run under a CMS and nine will say MySQL without thinking. The tenth shipped a blog on SQLite two years ago, never touched it again, and quietly wonders why everyone else is paying for managed database instances.

This guide takes the SQLite vs MySQL CMS question seriously instead of defaulting to habit. TL;DR: for a single-server, read-heavy site — a blog, a marketing site, docs, a portfolio — SQLite is usually the better choice. It's faster for reads, backs up with cp, and uses zero extra RAM. MySQL earns its complexity once you have many concurrent writers, multiple app servers, or replication requirements. Most CMS installs never get there.


The Architectural Difference: a File vs a Daemon

The real difference between SQLite and MySQL isn't features or SQL dialect. It's architecture. SQLite is a library that reads and writes a single file inside your application's process. MySQL is a separate server program your application talks to over a socket. Everything else — performance, ops, backup strategy — falls out of that one distinction.

With SQLite, there is no database server. Your CMS opens database.sqlite the way it would open any file, and the SQLite library (compiled into PHP itself) handles queries in-process. No port, no credentials, no daemon to start, monitor, or restart after a crash.

MySQL runs as mysqld, a long-lived process with its own memory pools, user accounts, config file, and failure modes. Your CMS connects over TCP or a Unix socket, authenticates, and sends queries across that connection.

Neither model is "better" in the abstract. SQLite's job is to be the data layer for one application on one machine. MySQL's job is to be a shared data service for many clients, possibly on many machines. A typical CMS install — one app, one machine — matches SQLite's job description exactly. We just forgot that because the LAMP era hardcoded "M = MySQL" into everyone's muscle memory.


Why SQLite's Bad Reputation Is Outdated

SQLite's "toy database" reputation comes from two real limitations that were fixed or made irrelevant years ago. Since WAL mode shipped in SQLite 3.7.0 (2010), readers no longer block writers, and the SQLite team itself states the engine works fine for sites serving around 100,000 hits per day — which covers the vast majority of sites on the internet.

The "It Locks the Whole Database" Myth

This one was true — fifteen years ago. In the old rollback-journal mode, a write locked the entire database, and readers had to wait. Painful for anything interactive.

WAL (write-ahead logging) mode changed the model: writes append to a separate log file while readers keep reading the main file. Readers don't block the writer; the writer doesn't block readers. One PRAGMA journal_mode=WAL; and the most-cited SQLite complaint disappears. Laravel-based stacks typically enable it as a connection option, and it persists in the database file once set.

The "It Can't Handle Traffic" Myth

The SQLite project's own documentation is blunt about this: SQLite "works great as the database engine for most low to medium traffic websites (which is to say, most websites)," and gives ~100K hits/day as a conservative figure — adding that it has been demonstrated to handle 10x that.

For context: sqlite.org itself runs on SQLite, and every Android phone, iPhone, and browser on earth runs it billions of times a day. It is plausibly the most deployed and most tested database software in existence. A blog doing 100K hits/day is a successful blog — the database will not be the thing that breaks first.

What Actually Remains True

Honesty matters here, so the real remaining limits:

  • One writer at a time. WAL fixed reader/writer blocking, but two simultaneous writes still queue. More on why this rarely matters below.
  • One machine. SQLite is a local file. Two app servers can't safely share it over NFS. Multiple application servers → SQLite is out, no argument.
  • No network access. You can't point an external BI tool at it the way you can a MySQL host.

These are real. They're also irrelevant to a single-VPS blog, which is what most self-hosted CMS installs actually are.


Where SQLite Wins for a CMS

For a CMS on one server, SQLite wins on four concrete fronts: zero configuration, single-file backups, no per-request connection overhead, and zero standing RAM cost. Each of these is a daily operational difference, not a benchmark curiosity.

Zero Configuration

A SQLite-backed CMS install is: upload files, point the config at a file path, done. No CREATE DATABASE, no user grants, no "Error establishing a database connection" page when the daemon hiccups. Anyone who has supported WordPress installs knows how much setup pain lives in that one connection step.

Backups Are Literally cp

A SQLite database is one file. Your entire content database — posts, pages, users, settings — backs up with:

sqlite3 database.sqlite ".backup backup-$(date +%F).sqlite"

(or plain cp when the site is quiet). Restore is copying the file back. Compare the MySQL ritual: mysqldump with the right flags, credentials, locking considerations, and a restore procedure you'd better have tested before you need it. For a one-person site, "the backup is a file" removes a whole category of 2 a.m. problems.

No Connection Overhead

Every MySQL query rides on a connection that had to be opened, authenticated, and managed. SQLite queries are function calls into a library already loaded in your process — no network hop, no inter-process round trip per query.

A CMS page render commonly issues 10–40 queries (post, author, categories, menus, settings, related posts). At ~a millisecond of round-trip overhead per query on a client-server setup, that's tens of milliseconds spent on transport alone — overhead SQLite simply doesn't have.

No Daemon Eating Your VPS

mysqld on a default config idles at 300–500 MB of resident memory. On a $5/month VPS with 1–2 GB of RAM, that's a quarter or more of the machine doing nothing but waiting for queries — RAM that could go to PHP-FPM workers or the page cache. Drop MySQL and a small VPS suddenly feels a size bigger. This is the same logic that draws people to flat-file systems — we compared the broader trade-offs in flat-file CMS vs database CMS — except SQLite keeps real SQL, real indexes, and real queries while still being "just files" operationally.


Where MySQL Wins (and Genuinely Earns Its Keep)

MySQL isn't legacy baggage — it solves real problems SQLite structurally can't. Choose MySQL when you have many concurrent writers, more than one app server, replication or high-availability requirements, a hosting panel that hands you managed MySQL, or datasets growing past tens of gigabytes.

Many Concurrent Writers

A forum, a store taking orders, a CMS where a 20-person editorial team saves drafts all day — these generate constant, overlapping writes. MySQL's row-level locking (InnoDB) lets those writes proceed in parallel; SQLite serializes them. Under heavy write load that queue becomes your bottleneck.

Multiple App Servers

Two app servers behind a load balancer need a database both can reach over the network. That's the client-server model's home turf. SQLite has no answer here, and pretending otherwise (file shares, sync hacks) ends badly.

Replication and HA

MySQL replicas give you read scaling, hot standbys, and point-in-time recovery. If downtime costs real money per minute, that machinery is worth its price. SQLite's answer is "restore the file from backup" — fine for a blog, unacceptable for a payments platform.

The Hosting-Panel Reality

A practical, unglamorous point: most shared hosting (cPanel and friends) gives you managed MySQL — provisioned, patched, and backed up by the host. If someone else is doing the ops for free, the "SQLite is less ops" argument weakens. On shared hosting, MySQL is the path of least resistance; on a bare VPS, it's the opposite.

Very Large Datasets

SQLite's theoretical ceiling is enormous (the format supports up to 281 TB), but practical comfort tops out lower. Past tens of gigabytes, MySQL's tooling — online schema changes, per-table tuning, mature monitoring — starts paying for itself. A CMS database hits that scale roughly never; 10,000 posts is maybe 200 MB.


The Write-Concurrency Reality: How Often Does a Blog Actually Write?

Here's the question that decides the whole debate: how often does your CMS write? A typical blog publishes a few posts a week and serves thousands of reads per post — a read/write ratio north of 10,000:1, the workload where SQLite's single-writer limit is irrelevant.

Walk through a real day for a content site:

  1. Visitors read pages — thousands of SELECTs. SQLite in WAL mode handles unlimited concurrent readers.
  2. You publish a post — a handful of INSERTs and UPDATEs over a few hundred milliseconds.
  3. Someone submits a contact form — one INSERT.
  4. A scheduled job updates a counter — one UPDATE.

The "one writer at a time" limit means that if the form INSERT and the counter UPDATE land in the same instant, one waits a few milliseconds (with busy_timeout set, queuing is automatic and invisible). That's the entire cost. No page load slows down; no request fails.

The workload people imagine when they hear "concurrency" — hundreds of users writing simultaneously — is a different kind of site. If that's yours, you're running an app, not a blog, and you should be on MySQL. Know which site you have.


Benchmarks, Honestly Framed

Database benchmarks are where marketing goes to lie, so let's keep this honest. For the queries a CMS actually runs — indexed single-row lookups and small joins on one machine — SQLite is typically as fast or faster than MySQL, because every MySQL query pays a network/IPC round trip and SQLite queries are in-process function calls.

What honest framing requires:

  • Reads favor SQLite on one box. No handshake, no round trip, no result-set serialization across a socket. The per-query saving is sub-millisecond, but a CMS page issues dozens of queries, so it compounds.
  • Bulk and concurrent writes favor MySQL. Row-level locking and parallel writers keep throughput up under write pressure where SQLite would serialize.
  • Most "X is 10x faster" numbers are workload artifacts. A benchmark of 1,000 sequential INSERTs punishes SQLite if each is its own transaction (fsync per write) and flatters it if they're batched. Neither resembles CMS traffic.
  • Cache makes most of it moot. Any sensible CMS caches rendered pages; on a cache hit the database isn't even in the request path. The benchmark that matters is your p95 page render, not synthetic queries-per-second.

Our experience building UnfoldCMS matches the docs: it runs on either engine — SQLite or MySQL is a Laravel .env choice, not a code change — and on a small VPS, the SQLite install consistently renders uncached pages a little faster because the round trips are gone. The database-driven search works the same on both, so the engine choice doesn't cost you features.


Backup and Ops: The Boring Comparison That Decides It

Concern SQLite MySQL
Install None — it's a file Install + secure + configure daemon
Backup .backup / copy one file mysqldump or binlog tooling
Restore Copy the file back Import dump, check users/grants
Standing RAM ~0 ~300–500 MB idle
Monitoring Disk space Daemon health, connections, slow log, binlogs
Failure modes Disk full Daemon down, connection limits, auth, disk full
Remote access No Yes
Upgrades Ships with PHP Separate upgrade cycle

One war story for the right-hand column: MySQL binlogs on a default config can silently fill a small disk and take down every site on the box — we've lived it. The fix is one config line, but it's a line SQLite never asks you to know about. Every row in MySQL's column is solvable; with SQLite most rows don't exist.


The Migration Path: Start Small, Move When the Pain Is Real

The strongest argument for starting with SQLite is that the decision is reversible. Begin on SQLite; if you ever hit its real limits — sustained write contention or a second app server — move to MySQL. In a Laravel-based CMS, the swap is a config change plus a data copy, not a rewrite.

The shape of the move:

  1. Provision MySQL and create an empty database.
  2. Run your CMS migrations against it so the schema exists.
  3. Copy the data across (budget an afternoon; check boolean and datetime columns, since SQLite is loosely typed).
  4. Flip the connection in .env: DB_CONNECTION=mysql plus credentials.
  5. Smoke-test and go.

Because Laravel's query builder abstracts the SQL dialect, application code doesn't change. Keep the moving parts swappable and infrastructure decisions stop being scary — the same logic behind deploying a CMS site behind Netlify. It cuts the other way too: API-first systems that require a heavy database from day one are part of why people go looking for lighter Strapi alternatives.

The trap is the reverse reasoning: "we might need MySQL someday, so let's pay its ops cost now." That's buying an HA database for a site with one writer — you, on publish day.


Decision Table: Which Database for Which Site?

The comparison question, stated plainly: for each common site type, should the CMS run SQLite or MySQL?

Site type Pick Why
Personal blog SQLite Read-heavy, one writer, one box
Marketing / company site SQLite Few writes, easy backups, cheap VPS
Docs site SQLite Almost pure reads
Small agency client site SQLite Less ops per client = real margin
Shared-hosting install MySQL Panel provides it managed — use it
Editorial team (10+ writers) MySQL Constant overlapping draft writes
Community / forum / comments MySQL Many concurrent writers
Multi-server / load-balanced MySQL SQLite can't be shared across machines
E-commerce MySQL Orders are writes; HA matters

The pattern: content out = SQLite, content in = MySQL. Sites that mostly serve content thrive on the embedded file. Sites that mostly collect it need the client-server engine.


FAQ

Can SQLite handle production traffic for a CMS?

Yes. The SQLite docs state it works for low-to-medium traffic sites — roughly 100K hits/day conservatively, with 10x that demonstrated. For read-heavy CMS workloads with page caching, the database is rarely the bottleneck.

Is SQLite faster than MySQL?

For single-machine, read-heavy workloads, usually yes — queries are in-process calls with no network round trip. For concurrent write throughput, MySQL is faster. A blog is the first workload; a busy forum is the second.

When should I switch from SQLite to MySQL?

When you hit a structural limit: sustained concurrent writes, a second app server, or replication/HA requirements. Until one of those is real, the switch buys you ops cost and nothing else.

Does WAL mode fix SQLite's concurrency problems?

It fixes the big one: readers and the writer no longer block each other. Writes still serialize, but with busy_timeout set, brief overlaps queue automatically. For low-write CMS workloads this is invisible.

Which database does UnfoldCMS use?

Either. UnfoldCMS is a self-hosted Laravel CMS that runs on SQLite or MySQL — you pick in the .env file. It's shared-hosting compatible (where MySQL is the natural choice) and works on a bare VPS (where SQLite shines). One-time license, no per-database upcharge.


The Bottom Line

"CMS = MySQL" is a habit from an era when SQLite genuinely couldn't keep up. That era ended with WAL mode. The honest answer to "SQLite vs MySQL for a CMS" is: count your writers and count your servers. One writer, one server — SQLite, and enjoy the cp backups. Many writers or many servers — MySQL, and it'll earn its RAM.

If you want a CMS that doesn't force the choice, UnfoldCMS runs on either — start on SQLite on a $5 VPS, move to MySQL if your site outgrows it. See pricing for the one-time license.


Sources: SQLite official documentation ("Appropriate Uses for SQLite", "Write-Ahead Logging"), MySQL 8.0 Reference Manual (InnoDB locking), Laravel database documentation, and our own deployments of UnfoldCMS on both engines. Traffic figures are the SQLite project's published estimates; treat all single-number benchmarks — including ours — as workload-dependent.

Free & Open Source

Own your CMS. No subscriptions.

Unfold CMS is free to download and self-host. Built on Laravel + React, full source code included.

Share this post:

Discussion

Comments (0)

Leave a Comment

Please log in to leave a comment.

Don't have an account? Register here

No comments yet. Be the first to share your thoughts!

Keep Reading

Related Posts

Back to all posts
Powered by UnfoldCMS