PocketBase: The backend that fits in 1 file
2026-07-24
This site runs on PocketBase, and I mentioned that in passing in my last post. This time I want to go deeper: what PocketBase actually is under the hood, why its design choices matter, and - just as important - where I would not reach for it. Too many "why I chose X" posts skip the tradeoffs. This one won't.
What PocketBase actually is
PocketBase is a single, self-contained Go executable that bundles an embedded SQLite database, a REST and realtime API, authentication (email/password, OAuth2, OTP, MFA), file storage, an admin dashboard, and a rule-based authorization engine - all in one process, with no external services to stand up. You download one binary, run it, and you have a working backend with a database schema builder in front of you.
What makes it more than "SQLite with a UI" is that it's designed to be extended two different ways. If you need light scripting - custom routes, request hooks, scheduled jobs - you can write it in JavaScript, executed by an embedded Goja VM through pb_hooks/*.pb.js files, no recompilation needed (that's exactly how the routing and templating for this site is built). If you need something heavier - custom business logic, background workers, tight integration with other Go code - you import PocketBase as a Go package and build your own executable around it. That two-tier extensibility model is the thing most competitors in this space don't offer.
The architectural decision that explains everything else
The single most consequential design decision in PocketBase is using SQLite, via a pure-Go driver, as the primary datastore, embedded directly in the same process as the API server. No network hop between app and database, no connection pool to tune, no separate database server to deploy, patch, or pay for. Backups are a matter of copying a file (or streaming it continuously with a tool like Litestream). Local development is production-identical because there's no separate "dev database" topology to diverge from.
But this decision also defines the ceiling. SQLite allows many concurrent readers but serializes writers - even with WAL mode enabled (which PocketBase uses by default), only one write transaction commits at a time. For read-heavy workloads this is close to irrelevant; for write-heavy, high-concurrency workloads it becomes the bottleneck long before your CPU or memory does. And because the database lives on one machine's disk, PocketBase scales vertically, not horizontally. There's no built-in sharding, no multi-primary replication, no distributed transaction story. You scale by putting it on a bigger box, not by adding more boxes.
Where it's the right tool
- MVPs and side projects, where time-to-first-working-API matters more than five-year scaling headroom.
- Internal tools and admin panels, where the built-in dashboard alone replaces a chunk of custom CRUD UI you'd otherwise have to build.
- Personal sites and small content platforms - this blog being the obvious example - where traffic and write volume are modest and operational simplicity is worth more than theoretical scale.
- Mobile or desktop app backends for a small-to-medium user base, especially when you want auth, storage, and realtime sync without assembling four separate services.
- Self-hosted, data-sovereign deployments, where "my data lives in one file I control" is a feature, not a limitation.
- Edge and single-tenant deployments, where each customer or install gets its own isolated instance rather than sharing a multi-tenant cluster.
Where it's the wrong tool
- High write-concurrency multi-tenant SaaS, where hundreds of clients are writing simultaneously against shared data. SQLite's single-writer model will show up as latency long before you hit meaningful revenue-scale traffic.
- Systems that need horizontal scaling or geographic distribution - multi-region active-active writes, read replicas managed at the database layer, or sharding across nodes are not things PocketBase does for you.
- Heavy analytical or reporting workloads against large datasets - SQLite is a fine OLTP engine but isn't built for the query planner sophistication or parallelism of a proper analytical or even a mature OLTP engine like Postgres at scale.
- Teams that need a deep integration ecosystem - extensions, foreign data wrappers, mature ORMs, a large hiring pool of specialists - the kind of thing you get by default with Postgres-based platforms like Supabase.
- Anything where "one machine, one file" is unacceptable from a compliance or availability standpoint, without you personally building the replication and failover story on top.
How it compares
Against Firebase, PocketBase trades a hosted, infinitely-scaling proprietary backend for something self-hosted, open source, and inspectable - you own the SQLite file, you can read it with any tool that speaks SQL, and there's no vendor billing surprise. Against Supabase, the comparison is really "SQLite vs. Postgres, embedded vs. client-server" - Supabase gives you a real, horizontally-scalable-adjacent Postgres with a richer ecosystem, at the cost of more moving parts and a heavier default footprint. PocketBase's pitch is specifically for the projects where that extra machinery is overhead, not value.
The bottom line
PocketBase is not trying to be everything to everyone, and that focus is exactly what makes it good at what it does. If your project's constraints are "ship fast, keep operations simple, don't overpay for infrastructure you don't need yet," it's an excellent fit - which is precisely why it's what this site runs on. If your constraints are "we need to scale writes across regions for a few hundred thousand concurrent tenants," it's the wrong tool, and no amount of tuning will change the fundamentals of the architecture. Knowing which bucket your project falls into before you commit is the actual skill here - the tool choice is easy once you do.