Blog

Scaling PocketBase: From MVP to Production

Learn strategies for scaling your PocketBase application to handle thousands of concurrent users and millions of records.

Sarah Chen · Mar 4, 2024 · 3 min read performance scaling architecture
Scaling PocketBase: From MVP to Production

Scaling PocketBase: From MVP to Production

A common question we get is: “Can PocketBase scale?” The short answer is yes. The long answer involves understanding architecture, indexing, and resource management.

In this post, we’ll explore how to take a PocketBase app from a weekend prototype to a high-traffic production system.

The SQLite Myth

There’s a misconception that SQLite (PocketBase’s engine) is a “toy” database. In reality, SQLite is the most widely deployed database engine in the world. It powers:

  • Every Android & iOS device
  • Web browsers (Chrome, Firefox)
  • MacOS internals

SQLite can handle huge amounts of data. The “limitation” is usually write concurrency (only one writer at a time), but in WAL mode (which PocketBase uses), this is rarely a bottleneck for typical web apps until you reach massive scale.

Vertical Scaling (The Power of Simplicity)

PocketBase is designed to scale vertically. Because it’s a single Go binary with an embedded database, there is zero network latency between your API and your data.

To scale, you simply create a bigger server.

  • MVP: 1 vCPU, 512MB RAM ($5/mo)
  • Growth: 2 vCPU, 4GB RAM ($20/mo)
  • Production: 4 vCPU, 8GB RAM ($40/mo)
  • Enterprise: 16 vCPU, 32GB RAM ($100+/mo)

A high-performance dedicated server running PocketBase can handle 100,000+ realtime connections and 10,000+ requests per second. That’s more than enough for 99% of startups.

Database Optimization

Before buying a bigger server, optimize your data usage.

1. Indexes are Key

If your queries are slow, you’re probably missing an index. Go to Settings > Export collections to export your schema, but managing indexes is currently done via direct SQL or ensuring you indexed fields in the collection editor (searchable fields).

  • Note: PocketBase automatically indexes id, created, updated, and relation fields.

2. Indexes for Filter/Sort

If you frequently run: pb.collection('posts').getList(1, 10, { filter: 'category = "tech"', sort: '-views' })

You should ensure category and views are indexed.

3. Simplify Relations

Deeply nested relation expansion (expand=user.company.settings) increases query complexity. Try to denormalize data where appropriate or fetch related data in separate, simpler queries.

Performance Tips

Use the requestKey

The JS SDK has a request cancellation feature. If a user clicks a button 5 times, previous requests are cancelled.

// This prevents multiple identical checks racing each other
await pb.collection("users").getList(1, 1, {
  filter: 'email = "[email protected]"',
  requestKey: "email_check",
});

Static vs. Dynamic Content

Use a CDN (like PocketBase Cloud’s CDN or Cloudflare) to cache static assets. Your PocketBase server should only be handling dynamic API requests (JSON).

Limit Fields

Don’t fetch what you don’t need. pb.collection('posts').getList(1, 20, { fields: 'id,title,slug,author' }) This reduces JSON parsing overhead and saves bandwidth.

Conclusion

PocketBase scales incredibly well because it eliminates the complexity of distributed systems. For the vast majority of applications, a single vertically scaled server is more performant, cheaper, and easier to maintain than a complex microservices cluster.

Focus on your code efficiency and let raw CPU power handle the rest.