Back to News & Insights
Web Development August 12, 2026 · 7 min read

Horizontal Scaling: Solving One Problem, Creating Another

When learning system design, we are almost always fed the exact same doctrine: start with one API +...

Horizontal Scaling: Solving One Problem, Creating Another

When learning system design, we are almost always fed the exact same doctrine: start with one API + one Database, then as traffic increases, add a Load Balancer, Redis, a Separate Database Server, and eventually Read Replicas.

The problem is that this standard pattern is built on a single assumption: every user must crowd into the same shared database. However, if your application's data characteristics allow it to be segregated per user from day one, blindly following this pattern becomes a complexity trap that makes your system increasingly fragile.

In conventional architecture, all users share the same database tables. As server load increases, a developer's first reflex is usually to add more infrastructure layers:

While the initial intent is to solve scaling challenges, the execution creates a dramatic contradiction: Seeking Fault Tolerance, but Adding a Single Point of Failure

To distribute traffic, we introduce a Load Balancer and spin up additional API instances. However, because user requests now bounce between instances, their session state is lost.

The Absurd Result: We added a Load Balancer to make the system fault-tolerant, but now, if Redis crashes, the entire application goes down. Seeking Easy Scaling, but Paying with Complex Bugs

In theory, spinning up another API server is just a click of a button. However, once a single business transaction touches multiple servers simultaneously, standard database transactions (BEGIN...COMMIT) no longer work.

We are forced to implement complex patterns like Two-Phase Commit or the Saga Pattern. We chose horizontal scaling in pursuit of simplicity, but ended up trapped in race conditions and data inconsistency issues that are notoriously hard to debug in production.

Instead of forcing all data into one giant database, this approach offers a remarkably simple concept: give every user their own separate SQLite file.

So, what happens when the application needs public features like a feed, global search, or cross-user data aggregation?

Private source data remains safely stored in each user's individual file. Whenever a user publishes something, the application simply writes a lightweight metadata entry (write-on-publish) to a dedicated aggregate database. If this aggregate database becomes corrupted, its data can easily be re-indexed from the users' primary files.

By eliminating a separate database server and embedding the SQLite engine directly inside the application process, system mechanics shift naturally:

Data Leaks Become Structurally Impossible: In a shared database, a single bug forgetting a WHERE user_id = ? clause can leak data between users. In this approach, the application connection strictly opens the target user's file. The risk of data leakage is eliminated not by developer vigilance, but by physical file boundaries. Radically Simple Backups: You no longer need to execute gigabyte-sized database dumps or manage complex log replication pipelines. To back up a user's data, you simply stream or copy their single .db file to Object Storage (e.g., using Litestream). Zero Network Latency: Because the database engine runs inside the application process itself, every query is processed directly in memory and local disk. Network calls from the API server to the DB server—which typically consume milliseconds—vanish entirely.

A reasonable question arises: "If the database lives as local files, how do we handle traffic spikes? We can't just slap a Load Balancer in front of it."

In the modern cloud era, scaling CPU and RAM on a single server is often overlooked because it is deemed less fashionable than running dozens of small nodes. Yet, the logic behind vertical scaling is stronger than ever: a single modern machine can handle tens of thousands of requests per second provided its performance isn't bogged down by database network latency and distributed state synchronization.

The physical capacity of a single server today has reached almost absurd levels. On Google Cloud Platform (GCP), a standard Virtual Machine can offer up to 224 vCPUs. In Memory-Optimized categories (such as the M or X4 series), limits reach up to 1,920 vCPUs and a massive 32 Terabytes (32,768 GB) of RAM on a single machine. These specs prove that the physical ceiling of a single server far exceeds the compute needs of 99.9% of web applications on the market.

Based on this reality, the architecture divides infrastructure responsibilities pragmatically:

Compute (CPU/RAM): Scaled vertically. You leverage the capacity of a single machine to execute all application logic and embedded database operations in one place without network overhead. Storage (Disk): Scaled horizontally without limits. Storage capacity is not constrained by the machine itself; you can attach new block storage volumes (e.g., up to hundreds of Terabytes) instantly with zero downtime.

Want to discuss this further?

Book a free strategy call with our team to see how these insights apply to your specific business goals.

Book a consultation