Laravel Read/Write Splitting &amp; Sticky Reads | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com) [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles  ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

  [ home ](https://www.msaied.com)    [ articles ](https://www.msaied.com/articles)    Read/Write Splitting, Connection Pooling, and Sticky Reads in Laravel        On this page       1. [  Read/Write Splitting, Connection Pooling, and Sticky Reads in Laravel ](#readwrite-splitting-connection-pooling-and-sticky-reads-in-laravel)
2. [  Configuring Read/Write Connections ](#configuring-readwrite-connections)
3. [  The Sticky Option and Why It Exists ](#the-sticky-option-and-why-it-exists)
4. [  Connection Pooling: PgBouncer and RDS Proxy ](#connection-pooling-pgbouncer-and-rds-proxy)
5. [  Forcing a Specific Connection in Eloquent ](#forcing-a-specific-connection-in-eloquent)
6. [  Takeaways ](#takeaways)

  ![Read/Write Splitting, Connection Pooling, and Sticky Reads in Laravel](https://cdn.msaied.com/171/92ea6daa5c8b599f43ef1c69a7c1eaa6.png)

  #laravel   #database   #performance   #postgresql   #mysql  

 Read/Write Splitting, Connection Pooling, and Sticky Reads in Laravel 
=======================================================================

     14 Jun 2026      4 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Read/Write Splitting, Connection Pooling, and Sticky Reads in Laravel  ](#readwrite-splitting-connection-pooling-and-sticky-reads-in-laravel)
2. [  02   Configuring Read/Write Connections  ](#configuring-readwrite-connections)
3. [  03   The Sticky Option and Why It Exists  ](#the-sticky-option-and-why-it-exists)
4. [  04   Connection Pooling: PgBouncer and RDS Proxy  ](#connection-pooling-pgbouncer-and-rds-proxy)
5. [  05   Forcing a Specific Connection in Eloquent  ](#forcing-a-specific-connection-in-eloquent)
6. [  06   Takeaways  ](#takeaways)

 Read/Write Splitting, Connection Pooling, and Sticky Reads in Laravel
---------------------------------------------------------------------

Scaling a Laravel application past a single database node almost always means introducing a read replica. Laravel ships with first-class support for this, but the details matter: get them wrong and you'll chase phantom bugs caused by replication lag or exhaust your connection limit under load.

### Configuring Read/Write Connections

Laravel's `config/database.php` accepts a `read` and `write` key inside any connection. Both are merged with the top-level connection config, so you only override what differs.

```php
'pgsql' => [
    'driver' => 'pgsql',
    'read' => [
        'host' => [
            env('DB_READ_HOST_1', '10.0.1.11'),
            env('DB_READ_HOST_2', '10.0.1.12'),
        ],
    ],
    'write' => [
        'host' => env('DB_HOST', '10.0.1.10'),
    ],
    'sticky' => true,
    'database' => env('DB_DATABASE', 'app'),
    'username' => env('DB_USERNAME', 'app'),
    'password' => env('DB_PASSWORD', ''),
    'charset'  => 'utf8',
    'prefix'   => '',
    'schema'   => 'public',
],

```

When multiple hosts are listed under `read`, Laravel picks one at random per request — a simple client-side load balancer at zero infrastructure cost.

### The Sticky Option and Why It Exists

Replication is asynchronous. If you write a record and immediately read it back on a replica, the replica may not have caught up yet. Laravel's `sticky` option solves this: once a write connection is used during a request, all subsequent reads in that same request are also routed to the write connection.

```php
// Without sticky=true this SELECT might miss the just-inserted row
DB::table('orders')->insert(['user_id' => $userId, 'total' => 9900]);
$order = DB::table('orders')->where('user_id', $userId)->latest()->first();

```

With `sticky => true`, the second query goes to the primary. Without it, you're gambling on replication lag.

**When to disable sticky:** Background jobs that only read data, reporting queries, and read-heavy API endpoints that don't write. You can force a read connection explicitly:

```php
$stats = DB::connection('pgsql::read')->table('events')->count();
// Or use the query builder helper:
$stats = DB::table('events')->useReadPdo()->count();

```

`useReadPdo()` is available on the query builder and bypasses the sticky logic intentionally.

### Connection Pooling: PgBouncer and RDS Proxy

PHP-FPM and Octane workers open a new PDO connection per worker (or per request under FPM). At scale this exhausts `max_connections` on PostgreSQL fast.

**PgBouncer in transaction mode** is the standard fix for PostgreSQL:

```ini
; pgbouncer.ini
[databases]
app = host=10.0.1.10 dbname=app

[pgbouncer]
pool_mode = transaction
max_client_conn = 2000
default_pool_size = 25

```

Point Laravel's `write` host at PgBouncer's address. Each transaction borrows a server connection and returns it immediately — 2 000 PHP workers can share 25 real Postgres connections.

**Caveats with transaction-mode pooling:**

- `SET` statements and advisory locks are connection-scoped and will not survive across transactions.
- Laravel's `DB::statement('SET search_path TO tenant_schema')` pattern breaks. Use the `search_path` DSN option or schema-prefix your tables instead.
- Prepared statements must be disabled: set `'options' => ['PDO::ATTR_EMULATE_PREPARES' => true]` in your connection config, or configure PgBouncer's `server_reset_query`.

**RDS Proxy** handles MySQL and PostgreSQL and is transparent to Laravel — point your host at the proxy endpoint and it manages the pool server-side. It also handles IAM authentication and secret rotation without app changes.

### Forcing a Specific Connection in Eloquent

```php
class ReportQuery
{
    public function handle(): Collection
    {
        return Order::on('pgsql') // uses read replica via read/write split
            ->useReadPdo()
            ->where('created_at', '>=', now()->subDays(30))
            ->get();
    }
}

```

For models that should always write to primary regardless of sticky state:

```php
class AuditLog extends Model
{
    protected $connection = 'pgsql'; // write connection explicitly
}

```

### Takeaways

- Enable `sticky => true` whenever a request can write then immediately read its own data.
- Use `useReadPdo()` on reporting queries to explicitly bypass sticky and reduce primary load.
- PgBouncer in transaction mode dramatically reduces Postgres connection count but breaks session-level features — audit your app before enabling it.
- RDS Proxy is the managed alternative; it's transparent to Laravel but adds latency on cold connections.
- List multiple replica hosts in the `read` array for free client-side load balancing without a proxy.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Freadwrite-splitting-connection-pooling-and-sticky-reads-in-laravel&text=Read%2FWrite+Splitting%2C+Connection+Pooling%2C+and+Sticky+Reads+in+Laravel) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Freadwrite-splitting-connection-pooling-and-sticky-reads-in-laravel) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  Does Laravel's sticky option persist across queue jobs?        No. Each queued job runs in its own bootstrapped context. The sticky flag only tracks write usage within a single HTTP request or job execution cycle, so there is no cross-job contamination. 

      Q02  Can I use PgBouncer in session mode instead of transaction mode to avoid the SET statement problem?        Yes, session mode is safer and preserves all session-level state, but it provides far less connection multiplexing — each client holds a server connection for the entire session. For most PHP-FPM setups, transaction mode with emulated prepares is the better trade-off. 

      Q03  How do I verify which connection a query actually used?        Enable the query log with DB::enableQueryLog() and inspect DB::getQueryLog(), or listen to the Illuminate\Database\Events\QueryExecuted event, which exposes the connection name on the event object. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://www.msaied.com/articles) 

 [ ![The Pipeline Pattern in Laravel: Building Custom Pipelines Beyond Middleware](https://cdn.msaied.com/172/a5dea5e84a6eab22d5d7a76869aaecb4.png) laravel design-patterns pipeline 

### The Pipeline Pattern in Laravel: Building Custom Pipelines Beyond Middleware

Laravel's Pipeline class is far more than the engine behind HTTP middleware. Learn how to compose reusable, te...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 14 Jun 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/the-pipeline-pattern-in-laravel-building-custom-pipelines-beyond-middleware) [ ![Contextual Binding and Method Injection in Laravel's Service Container](https://cdn.msaied.com/170/396bdacb111411b734f3018920214c20.png) laravel service-container dependency-injection 

### Contextual Binding and Method Injection in Laravel's Service Container

Go beyond basic dependency injection. Learn how Laravel's contextual binding, tagged services, and method inje...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 14 Jun 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/contextual-binding-and-method-injection-in-laravels-service-container) [ ![PostgreSQL JSONB in Laravel: Indexing, Querying, and Casting Without the Chaos](https://cdn.msaied.com/169/5efebaf19646869da7c6064340c7d09f.png) laravel postgresql eloquent 

### PostgreSQL JSONB in Laravel: Indexing, Querying, and Casting Without the Chaos

JSONB columns are powerful but easy to misuse. Learn how to index, query, and cast PostgreSQL JSONB data in La...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 14 Jun 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/postgresql-jsonb-in-laravel-indexing-querying-and-casting-without-the-chaos) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://www.msaied.com)
- [Projects](https://www.msaied.com/projects)
- [Articles](https://www.msaied.com/articles)
- [Certificates](https://www.msaied.com/certificates)
- [Contact](https://www.msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
