top of page
DevCollar Private Limited Logo

How to Architect a SaaS Application for 1 Million Users

Writer: Devesh Singh
Devesh Singh
10 minutes ago
8 min read

Designing a SaaS application for 1 million users does not mean building every distributed-systems pattern on day one.

A million registered users may produce very different workloads depending on the product. A B2B dashboard used twice a week is different from a real-time collaboration platform with the same user count.

The right architecture starts with traffic, data, reliability, and growth requirements, then adds complexity only where measurements justify it.

This article walks through a practical SaaS architecture that can evolve toward large-scale usage without overengineering the first version.

Start With Workload, Not User Count

"1 million users" is not a useful infrastructure requirement by itself.

You need to estimate:

Registered users
        ↓
Monthly active users
        ↓
Daily active users
        ↓
Peak concurrent users
        ↓
Requests per second
        ↓
Read / write patterns

For example, two products may both have 1 million accounts:

SaaS A
1M registered users
50K daily active users
Mostly dashboard reads

SaaS B
1M registered users
400K daily active users
Real-time messaging and uploads

Their architectures will be very different.

Before scaling infrastructure, define expected peak traffic, storage growth, background workloads, file usage, geographic distribution, and availability requirements.

A Practical High-Level Architecture

A scalable SaaS application might evolve toward:

                 Users
                   ↓
             DNS / CDN
                   ↓
          Load Balancer / Edge
                   ↓
          Application Servers
          /        |         \
         ↓         ↓          ↓
      Cache    PostgreSQL    Queue
         │         │          ↓
         │         │       Workers
         │         │          ↓
         └─────────┴──── External Services

                   +
             Object Storage

Each component solves a specific problem.

The important part is that you do not necessarily need all of them immediately.

1. Keep the Application Layer Stateless

Application servers should avoid storing important user state only in local memory.

Bad:

User → Server A
         ↓
 Session stored only
 in Server A memory

If the next request reaches Server B, that state may not exist.

Prefer:

               ┌→ App Server A
User → Load ───┼→ App Server B
      Balancer └→ App Server C
                      ↓
             Shared Data Stores

Persist durable state in databases, object storage, or other appropriate shared systems.

This makes horizontal scaling much easier:

More traffic
    ↓
Add application instances

instead of redesigning the application whenever one server reaches its limit.

Start With Workload, Not User Count

"1 million users" is not a useful infrastructure requirement by itself.

You need to estimate:

Registered users
        ↓
Monthly active users
        ↓
Daily active users
        ↓
Peak concurrent users
        ↓
Requests per second
        ↓
Read / write patterns

For example, two products may both have 1 million accounts:

SaaS A
1M registered users
50K daily active users
Mostly dashboard reads

SaaS B
1M registered users
400K daily active users
Real-time messaging and uploads

Their architectures will be very different.

Before scaling infrastructure, define expected peak traffic, storage growth, background workloads, file usage, geographic distribution, and availability requirements.

Scalable SaaS architecture showing load balancing, stateless app servers, PostgreSQL, Redis, background workers, CDN, and monitoring

A Practical High-Level Architecture

A scalable SaaS application might evolve toward:

                 Users
                   ↓
             DNS / CDN
                   ↓
          Load Balancer / Edge
                   ↓
          Application Servers
          /        |         \
         ↓         ↓          ↓
      Cache    PostgreSQL    Queue
         │         │          ↓
         │         │       Workers
         │         │          ↓
         └─────────┴──── External Services

                   +
             Object Storage

Each component solves a specific problem.

The important part is that you do not necessarily need all of them immediately.

1. Keep the Application Layer Stateless

Application servers should avoid storing important user state only in local memory.

Bad:

User → Server A
         ↓
 Session stored only
 in Server A memory

If the next request reaches Server B, that state may not exist.

Prefer:

               ┌→ App Server A
User → Load ───┼→ App Server B
      Balancer └→ App Server C
                      ↓
             Shared Data Stores

Persist durable state in databases, object storage, or other appropriate shared systems.

This makes horizontal scaling much easier:

More traffic
    ↓
Add application instances

Instead of redesigning the application whenever one server reaches its limit.

Startups, a shared schema with carefully enforced tenant boundaries is the simplest starting point.

4. Add Caching Where Repeated Reads Justify It

A cache can reduce repeated database work.

Request
   ↓
Check Cache
  /     \
Hit     Miss
 ↓       ↓
Return  Database
          ↓
       Update Cache

Redis is commonly used for workloads such as:

  • Frequently requested data.

  • Rate-limit counters.

  • Temporary state.

  • Expensive computed results.

  • Short-lived application data.

But caching introduces invalidation and consistency problems.

Do not cache everything.

Start with measured hot paths where the performance benefit is clear.

5. Move Slow Work Out of HTTP Requests

Users should not wait for work that does not need to complete before the response.

Suppose signup triggers:

Create account
Send welcome email
Generate report
Sync CRM
Process analytics

Running all of this synchronously increases latency and makes the request depend on several external systems.

Instead:

User Request
     ↓
Application
     ↓
Create Required Data
     ↓
Queue Jobs
     ↓
Return Response

       Queue
         ↓
      Workers
      /  |   \
 Email Report CRM

Background jobs are useful for:

  • Email.

  • Notifications.

  • File processing.

  • Report generation.

  • Webhooks.

  • Data synchronization.

  • AI workloads.

  • Batch processing.

Design jobs to handle retries safely. For operations that may be retried, idempotency is especially important.

6. Put Static Assets and Files Outside Application Servers

Do not make your API server responsible for serving every image, video, or uploaded document.

Use object storage for files:

Client
   ↓
Object Storage

and a CDN for globally cacheable content:

User
  ↓
CDN
  ↓
Cached Asset

Cache Miss
  ↓
Origin / Object Storage

This reduces load on application servers and improves delivery latency for users in different regions.

For uploads, signed URLs can often allow clients to upload directly to object storage instead of routing large files through your application servers.

7. Scale Reads Before Sharding the Database

As database traffic grows, the first scaling step is rarely immediate sharding.

A common progression is:

Optimize Queries
      ↓
Add Correct Indexes
      ↓
Cache Hot Reads
      ↓
Improve Connection Management
      ↓
Read Replicas Where Useful
      ↓
Partition / Shard If Required

Read replicas can help workloads where a significant amount of traffic is read-heavy and can tolerate the consistency characteristics of replicas.

Sharding adds substantial complexity:

Which shard owns this tenant?
How do cross-shard queries work?
How are migrations handled?
How do we rebalance data?

Use it when the database workload actually requires it, not because the application has an ambitious user target.

8. Add Rate Limiting and Backpressure

At large scale, protecting the system is as important as serving normal traffic.

One client should not be able to consume all available capacity.

Rate limiting can be applied by:

IP
User
Tenant
API key
Endpoint

For example:

Client
   ↓
Rate Limit Check
   ↓
Allowed? ── No → 429
   ↓ Yes
Application

Also design background systems for backpressure.

If workers can process 1,000 jobs per minute but producers suddenly create 50,000, the queue will grow.

Monitor queue depth and processing time so overload becomes visible before it turns into a larger incident.

9. Design for Failure

At 1 million users, failures are normal.

A dependency will eventually become slow or unavailable.

Production code should assume:

Network calls fail
Requests time out
Jobs are retried
Servers restart
Deployments go wrong
External APIs become unavailable

Useful patterns include:

  • Timeouts.

  • Retries with appropriate backoff.

  • Idempotency.

  • Graceful degradation.

  • Health checks.

  • Redundancy.

  • Circuit-breaking where justified.

Retries need limits. Retrying an already overloaded dependency aggressively can make an incident worse.

10. Build Observability Before You Need an Incident

At scale, "the app is slow" is not enough information.

You need to identify where time is being spent:

Client
  ↓ 40 ms
CDN
  ↓ 20 ms
Application
  ↓ 80 ms
Database
  ↓ 600 ms

At minimum, monitor:

  • Request latency.

  • Error rate.

  • Request volume.

  • CPU and memory.

  • Database latency.

  • Slow queries.

  • Connection usage.

  • Cache hit rate.

  • Queue depth.

  • Worker failures.

  • External API latency.

Centralized logs and distributed tracing become increasingly useful as requests cross more components.

Observability should answer:

What is failing, where is it failing, and which users are affected?

Do You Need Microservices?

Not because you have 1 million users.

A modular monolith can scale horizontally and serve large workloads.

Start with:

             Application
     ┌──────────┼──────────┐
     ↓          ↓          ↓
   Auth       Billing    Projects
     └──────────┼──────────┘
                ↓
            PostgreSQL

Split services when there is a concrete reason, such as:

  • Independent scaling requirements.

  • Clear ownership by different teams.

  • Different reliability requirements.

  • Strong domain boundaries.

  • Independent deployment becoming valuable.

For example, a CPU-heavy media-processing workload may deserve separate workers or a service long before the rest of the application needs microservices.

Architecture should follow bottlenecks and organizational boundaries.

Plan for Data Growth

Traffic is only one dimension of scale.

A SaaS application may accumulate:

Users
Events
Audit logs
Files
Messages
Analytics
AI embeddings

Do not let high-volume event or analytics data accidentally overload the primary transactional database.

Separate workloads when their access patterns become meaningfully different.

For example:

Transactional Data → PostgreSQL
Files              → Object Storage
Cache              → Redis
Search             → Search Index, if needed
Analytics Events   → Analytics Pipeline, if needed

The phrase "if needed" matters.

Every additional datastore creates another system to operate, secure, back up, monitor, and understand.

Think About Geographic Scale Separately

One million users in one region is different from one million users distributed globally.

Start with CDN and edge delivery for static content.

For APIs, a single well-chosen primary region may be enough until latency or availability requirements justify a multi-region architecture.

Multi-region databases and active-active systems introduce difficult consistency and operational trade-offs.

Do not adopt them simply because the product is accessible worldwide.

Measure regional latency and business requirements first.

Security Must Scale With the Product

A SaaS architecture is incomplete without security.

Important foundations include:

  • Strong authentication.

  • Server-side authorization.

  • Tenant isolation.

  • Encryption in transit.

  • Encryption at rest where appropriate.

  • Secrets management.

  • Audit logging for sensitive operations.

  • Dependency and vulnerability management.

  • Backups and restore testing.

  • Least-privilege access.

Never trust a tenant ID sent by the client without verifying that the authenticated user is allowed to access that tenant.

Scaling a security mistake to 1 million users is much worse than scaling a slow query.


A Sensible Evolution Path

You do not need the final architecture on day one.

Stage 1: Early Product

Client
  ↓
CDN
  ↓
Application
  ↓
PostgreSQL
  ↓
Object Storage

Keep the application modular and deployable as one unit.

Stage 2: Growing Traffic

Add components where measurements justify them:

             Client
                ↓
               CDN
                ↓
          Load Balancer
                ↓
      Multiple App Instances
          /      |      \
         ↓       ↓       ↓
      Redis  PostgreSQL Queue
                         ↓
                      Workers

Stage 3: Large Workload

Potential additions:

CDN / Edge
     ↓
Application Fleet
     ↓
Cache + Queue
     ↓
Primary DB + Replicas
     ↓
Specialized Storage Where Needed

Only after real bottlenecks appear should you consider more expensive changes such as sharding, regional data placement, or service decomposition.

Capacity Planning for 1 Million Users

Do not size infrastructure from registered-user count.

Create estimates for peak usage.

For example:

1,000,000 registered users
        ↓
100,000 daily active users
        ↓
20,000 active during peak hour
        ↓
Average actions per active user
        ↓
Estimated peak requests/sec

Then load-test important paths.

Measure:

API throughput
P95 / P99 latency
Database CPU
Database connections
Cache performance
Queue throughput
Error rate

The exact numbers will differ for every SaaS product.

Capacity planning is useful because it turns architecture discussions from:

"Will this scale?"

into:

"At what measured load does this component become the bottleneck?"

Common Mistakes

Building for 1 Million Users Before You Have 1,000

Design for evolution, but do not operate unnecessary infrastructure.

Using Microservices as a Scaling Strategy

Services can help organizational and workload isolation, but they do not automatically make inefficient code scalable.

Ignoring Database Queries

The primary bottleneck in many applications is not the web framework. It is inefficient data access.

Caching Without a Strategy

A cache adds another copy of data. Define expiration and invalidation behavior.

Running Everything Synchronously

Move non-critical slow work to background processing.

Scaling Without Observability

If you cannot measure the system, you cannot reliably know what to scale.

Assuming User Count Equals Traffic

Architecture should be based on actual workload.

A Practical Architecture Checklist

Before claiming your SaaS is ready to scale, ask:

Area Question

Application Can instances scale horizontally?

Database Are important queries indexed and measured?

Multi-tenancy Is tenant isolation enforced consistently?

Cache Are only useful hot paths cached?

Jobs Is slow asynchronous work moved to workers?

Files Are uploads and static assets outside app servers?

Reliability Do external calls have sensible failure handling?

Security Are authorization and tenant boundaries enforced server-side?

Observability Can you locate latency and errors quickly?

If the answers are yes, you are usually in a much better position than a system with a complicated architecture but no measurements.

Conclusion

Architecting a SaaS application for 1 million users is not about selecting the largest possible infrastructure stack.

Start with a modular application, a strong relational database, object storage, and good operational foundations. Keep application instances stateless so they can scale horizontally. Add caching, queues, workers, replicas, and specialized systems when actual workload requires them.

Most importantly, measure the system.

The architecture that reaches 1 million users successfully is usually not the one that predicted every future problem. It is the one that stayed simple enough to evolve and added complexity only when real bottlenecks made the trade-off worthwhile.


Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating

Join the DC Club

Join our email list and get access to specials deals exclusive to our subscribers.

Thanks for submitting!

© 2026

 by DevCollar Private Limited

bottom of page