Reader Stacks

Multiple Database Connections in Laravel

A second, separate database — legacy data, a read replica, a reporting warehouse — is configured as another named connection, and a model or query explicitly opts into using it.

Laravel's default setup assumes one database, but the config supports any number of named connections simultaneously — genuinely useful for reading from a legacy system's database, connecting to a separate reporting/analytics warehouse, or splitting reads across a replica while writes go to a primary.

1. Configuring a second connection

// config/database.php
'connections' => [
    'mysql' => [
        'driver' => 'mysql',
        'host' => env('DB_HOST'),
        'database' => env('DB_DATABASE'),
        // ... the default connection
    ],

    'legacy' => [
        'driver' => 'mysql',
        'host' => env('LEGACY_DB_HOST'),
        'database' => env('LEGACY_DB_DATABASE'),
        'username' => env('LEGACY_DB_USERNAME'),
        'password' => env('LEGACY_DB_PASSWORD'),
    ],
],
// .env
LEGACY_DB_HOST=legacy-db.internal
LEGACY_DB_DATABASE=old_system
LEGACY_DB_USERNAME=readonly_user
LEGACY_DB_PASSWORD=...

2. Using a specific connection with the query builder directly

use Illuminate\Support\Facades\DB;

$legacyCustomers = DB::connection('legacy')->table('customers')->get();

3. Using a specific connection on an Eloquent model

class LegacyCustomer extends Model
{
    protected $connection = 'legacy';
    protected $table = 'customers';
}
$customers = LegacyCustomer::where('active', 1)->get();

A model with $connection set always queries that specific named connection, regardless of what the app's default connection is — this is the cleanest approach when a whole model consistently belongs to a separate database, rather than switching connections ad hoc per query.

4. Switching connection at query time instead

$customer = Customer::on('legacy')->find(1);

->on('legacy') is useful when the same model class occasionally needs to query a different connection situationally, rather than a model that's permanently tied to one non-default connection.

5. Cross-database queries don't work the way a normal JOIN does

A standard Eloquent relationship or a SQL JOIN assumes both tables live in the same database connection — querying across two genuinely separate database connections (not just separate schemas on the same server) can't be done with a single JOIN query. It typically requires querying each connection separately and combining the results in PHP, which is slower and more work than a same-connection join, and worth designing around rather than working with routinely if it can be avoided.

6. Common real use cases

  • Legacy system integration — reading from an old system's database during a migration period, without merging its schema into the main app.
  • Reporting/analytics database — a separate read-heavy warehouse, isolated from the primary transactional database so heavy reporting queries don't compete for resources with normal application traffic.
  • Multi-tenant databases — a separate physical database per tenant, rather than a shared database with a tenant ID column, for tenants needing genuine data isolation.

7. Transactions don't span multiple connections

A DB::transaction() block only covers operations on the connection it's called against — writes to two different named connections inside what looks like one transaction aren't actually atomic together; a failure partway through can leave one connection's changes committed and the other's rolled back. This is a real constraint worth designing around when a genuine cross-database atomic operation seems necessary.

Topics: Database Queries & Eloquent