Mahamudul Hasan Rubel
HomeBlogCoursesAboutProjectsSkillsExperiencePhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • Blog
  • Courses
  • About
  • Projects
  • Skills
  • Experience
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

Subscribe to the newsletter

Get new articles and course lessons delivered to your inbox. No spam, unsubscribe anytime.

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
Tech NewsJune 26, 20264 min read

Laravel Database Read Replicas: Scaling Postgres Effectively

Master Laravel database read replicas to scale Postgres. Learn how to manage connection switching, handle replication lag, and ensure data consistency.

LaravelPostgresDatabaseScalingHigh AvailabilityPHPNewsTrendsIndustry

When my team hit a wall with database contention during a flash sale, we realized our single Postgres instance was gasping for air. We had two choices: vertical scaling, which is a temporary band-aid, or horizontal scaling. We chose the latter, implementing Laravel database read replicas to offload the heavy read traffic while keeping writes on our primary node.

The setup is surprisingly straightforward, but the operational reality—specifically dealing with the "I just saved this record, why can't I see it?" bug—is where the real engineering begins.

Understanding Laravel Database Read Replicas

Laravel has built-in support for read-write splitting. You don't need a complex proxy like HAProxy or PgBouncer to start, though they help later. You configure your config/database.php to define a read array within your connection settings.

Here is how our production config looks for a standard Postgres setup:

PHP
'pgsql' => [
    'driver' => 'pgsql',
    'url' => env('DATABASE_URL'),
    'host' => env('DB_HOST', '127.0.0.1'),
    'read' => [
        'host' => [
            env('DB_READ_HOST_1'),
            env('DB_READ_HOST_2'),
        ],
    ],
    'write' => [
        'host' => [
            env('DB_HOST'),
        ],
    ],
    'sticky' => true,
    #6A9955">// ...
],

The sticky option is a lifesaver. It tells Laravel that if you’ve performed a write operation during the current request cycle, it should use the write connection for any subsequent reads. This solves the immediate consistency problem where a user updates their profile and immediately refreshes to see the old data because the replica hasn't caught up yet.

The Reality of Postgres Replication Lag

While Postgres replication is incredibly fast, it isn't instantaneous. We usually see a delay of around 50ms to 150ms in our AWS environment. That sounds negligible, but it’s an eternity if you’re redirecting users immediately after a write.

We initially tried to handle this by manually forcing a connection switch using DB::connection('pgsql')->statement(...), but that quickly turned into a maintenance nightmare. If you're struggling with similar architectural bottlenecks, you might want to look into Laravel Read-Write Splitting: Deterministic Connection Routing Guide to keep your routing logic clean.

Managing Distributed Data Consistency

Once you move to horizontal scaling Laravel apps, you have to accept that your system is now eventually consistent. If you have a critical path where the user must see their data immediately after a write, you have a few options:

  1. Stickiness: Keep the sticky => true flag enabled in your database config.
  2. Read-after-write: For specific critical queries, bypass the replica by using DB::connection('pgsql')->useWritePdo()->table('...')->get().
  3. Transactional Outbox: For background tasks, use the Transactional Outbox Pattern in Laravel: Ensuring Data Consistency to ensure that your events and database state don't drift apart.

We once tried to use a "global" flag to force all reads to the primary for a specific user role. It broke under load because the primary was still the bottleneck. We learned that it’s better to selectively route queries than to treat the database as a monolith.

Database Connection Switching Pitfalls

The most common mistake I see is developers forgetting that database connection switching happens at the PDO level. If you open a transaction, Laravel automatically pins that transaction to the write connection.

However, if you're using raw queries or third-party packages that don't respect the sticky setting, you might accidentally query a replica during an active transaction. Always verify your DB::getQueryLog() in your local environment to see exactly which host is hitting which query.

If your system relies on complex event-driven updates, you might find that Laravel Transactional Outbox with Change Data Capture for Consistency is a much cleaner way to handle data synchronization than trying to force synchronous reads.

FAQ: Common Concerns

Does this setup work with migrations? Yes, Laravel migrations always run on the write connection. You don't need to worry about schema changes hitting the replicas mid-migration.

What happens if a replica dies? Laravel’s default behavior is to try the next host in the array. If you have load balancer health checks, you should ensure your replicas are pulled from the rotation before they crash.

Is it worth the complexity? If your read-to-write ratio is 10:1 or higher, absolutely. If your database is under 20% CPU usage, you’re just adding points of failure without a performance gain.

We’re still tuning our connection pool sizes. I’m currently experimenting with fine-tuning the min_pool_size to prevent connection spikes during high-traffic events. It's a constant game of balancing performance against the overhead of maintaining these extra nodes.

Back to Blog

Similar Posts

Tech NewsJune 26, 20263 min read

Postgres WAL tuning for high-throughput event streams

Master Postgres WAL tuning to handle high-throughput event streams. Learn how to balance disk I/O, checkpoint frequency, and data integrity for better performance.

Read more
Close-up of a hand adjusting a chess clock during a timed chess game indoors.
Tech NewsJune 20, 20264 min read

Postgres logical decoding for real-time CDC: A Practical Guide

Master Postgres logical decoding for real-time CDC. Learn how to stream database changes effectively to build robust, event-driven architectures today.

Read more
Tech NewsJune 25, 20264 min read

Postgres Partial Indexes: Optimizing High-Traffic Query Performance

Postgres partial indexes are a high-impact way to boost database performance. Learn how to optimize query speed and cut storage costs by indexing specific rows.

Read more