$ lexprog.com

// notes from an old coder -- php, databases, and the occasional rant

[July 19, 2024] PostgreSQL

Setting Up PostgreSQL for Laravel

PostgreSQL for Laravel: Setup Tips

────────────────────────────────────────────────────────

PostgreSQL for Laravel: Setup Tips

Tip: Use pg_hba.conf for Access Control

Don't just open up all connections. Configure pg_hba.conf:

# Only local connections
local   all   all   md5
host    all   all   127.0.0.1/32   md5

Gotcha: PostgreSQL is Case-Sensitive for Identifiers

SELECT * FROM "Users"; -- Different from "users"

Laravel quotes identifiers, so this usually isn't an issue. But raw queries can bite you.

Tip: Use pg_stat_statements for Query Monitoring

CREATE EXTENSION pg_stat_statements;
SELECT query, calls, total_exec_time FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;

Shows your slowest queries.

Gotcha: SERIAL vs BIGSERIAL

Laravel uses BIGSERIAL for auto-increment IDs. Don't change this — SERIAL maxes out at 2 billion rows.

Tip: Connection Pooling with PgBouncer

For 100+ concurrent connections, use PgBouncer:

pool_mode = transaction
max_client_conn = 200
default_pool_size = 20

Gotcha: ON CONFLICT for Upserts

Post::updateOrCreate(
    ['slug' => $slug],
    ['title' => $title]
);

Generates INSERT ... ON CONFLICT under the hood.

Tip: Schema Search Path

'schema' => 'public,app',

Search multiple schemas without qualifying table names.

Gotcha: PostgreSQL Doesn't Support IF NOT EXISTS for Columns

Use raw SQL or check before adding:

if (! Schema::hasColumn('posts', 'slug')) {
    Schema::table('posts', fn($t) => $t->string('slug'));
}

Tip: EXPLAIN (ANALYZE, BUFFERS) Is Your Best Friend

For query debugging, always use EXPLAIN (ANALYZE, BUFFERS) instead of plain EXPLAIN. The BUFFERS option shows hit/miss rates for every node, revealing whether your indexes are actually in memory.

Tip: Partial Indexes Are Underutilized

CREATE INDEX ON orders (status) WHERE status = 'pending' creates a tiny index that covers only the rows your query needs. It's faster to scan and cheaper to maintain than a full-column index.

Gotcha: NULL Sorting Is Non-Obvious

By default, NULLs sort AFTER non-null values in ascending order. ORDER BY col DESC puts NULLs FIRST. Use NULLS LAST or NULLS FIRST to be explicit.

Senior Insight

The most common PostgreSQL setup mistake I encounter is inadequate shared_buffers configuration. Teams leave it at the default 128MB, then wonder why their database uses only a fraction of available RAM. As a rule of thumb, set shared_buffers to 25% of total RAM on a dedicated database server. But here's what I learned the hard way: setting it above 8GB on Linux requires kernel.shmmax adjustment, otherwise PostgreSQL refuses to start.

Source: pganalyze Blog (https://pganalyze.com/blog), PostgreSQL Docs (https://www.postgresql.org/docs/current/), Crunchy Data Blog (https://www.crunchydata.com/blog)

────────────────────────────────────────────────────────
<-- back to posts