← Docs

Database Setup

Preparing PostgreSQL for Gold Lapel. Most of it is optional — all of it is worth knowing.

Gold Lapel works with any standard PostgreSQL installation. These optional configurations unlock additional capabilities — faster startup, better write detection, broader index types, and instant matview refresh. If you have five minutes before your first run, this page will help you spend them well.

Nothing here is strictly required — Gold Lapel will start, proxy queries, and begin optimizing without any of these settings in place. That said, wal_level = logical is strongly recommended for cache correctness, and is worth prioritizing if you can only configure one thing. Gold Lapel will tell you, clearly, what it could not do and exactly how to enable it. If you prefer to prepare the ground before the first connection, this is the complete checklist.

Extensions

Seven PostgreSQL extensions give Gold Lapel access to capabilities it cannot replicate on its own. Each one is independent — install whichever ones your environment supports. Full-text search indexing (tsvector) is built-in and requires no extension.

Gold Lapel auto-creates extensions at startup when the connected user has sufficient privileges. If it cannot, it logs the exact command you would need to run. The information below is for those who prefer to set things up in advance, or whose environment requires manual extension management.

pg_stat_statements

Auto-installed by GL

Query history bootstrap — lets Gold Lapel read your historical query stats on first connection, skipping the warm-up period.

This extension tracks execution statistics for every SQL statement that runs against your database — call count, total time, mean time, rows returned. PostgreSQL maintains this data across restarts.

When Gold Lapel connects to a database with pg_stat_statements available, it reads your historical query stats immediately. Your hottest queries are identified before Gold Lapel observes a single live request. Materialized views can be created for high-impact patterns within the first refresh cycle — no warm-up period.

Without it: Gold Lapel learns query patterns from scratch by observing live traffic through the proxy. For busy applications this takes seconds. For quieter ones, perhaps a few minutes. The extension simply provides a head start.

Install

-- 1. Add to postgresql.conf:
shared_preload_libraries = 'pg_stat_statements'
-- 2. Restart PostgreSQL
-- 3. Create the extension:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

Part of the PostgreSQL contrib package — available on virtually every provider and installation. Requires shared_preload_libraries and a PostgreSQL restart because it needs to allocate shared memory at startup. Many managed providers enable it by default — you may find it already installed.

pg_trgm

Auto-installed by GL

Trigram indexes for LIKE/ILIKE — enables index-backed wildcard text searches that would otherwise require sequential scans.

Enables GIN trigram indexes for LIKE, ILIKE, and %pattern% queries. Standard B-tree indexes are useless for leading-wildcard searches — PostgreSQL must fall back to a sequential scan every time. Trigram indexes solve this by indexing three-character subsequences, allowing the planner to use an index for patterns that would otherwise require reading every row.

When Gold Lapel detects wildcard text searches hitting the same columns repeatedly, it creates trigram indexes that make those searches dramatically faster. A WHERE name ILIKE '%smith%' that previously scanned the entire table can use an index instead.

Without it: LIKE and ILIKE queries are proxied to PostgreSQL as normal. They work, but wildcard searches remain sequential scans. The queries are not broken — they are simply slower than they need to be.

Install

CREATE EXTENSION IF NOT EXISTS pg_trgm;

Part of the PostgreSQL contrib package. Available on all major providers. No restart required — this is the easiest to install.

fuzzystrmatch

Auto-installed by GL

Phonetic search indexes — enables expression indexes on soundex() and dmetaphone() for fuzzy name matching.

Phonetic functions like soundex() and dmetaphone() map words to phonetic codes — "Smith" and "Smyth" produce the same code, so WHERE soundex(name) = soundex('Smyth') matches both spellings. Without an index, PostgreSQL must compute the phonetic code for every row on every query.

When Gold Lapel detects repeated phonetic equality queries, it creates B-tree expression indexes on the function output — turning sequential scans into fast index lookups.

Without it: Phonetic queries are proxied to PostgreSQL as normal. They work, but remain sequential scans. Gold Lapel cannot create phonetic indexes without the extension.

Install

CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;

Part of the PostgreSQL contrib package. Available on all major providers. No restart required.

pgvector

Auto-installed by GL

Vector similarity HNSW indexes — enables index-backed approximate nearest neighbor search for <=> cosine distance queries on embeddings.

Enables HNSW indexes for vector similarity search. When Gold Lapel detects repeated vector search queries — for example, ORDER BY embedding <=> '[0.1, 0.2, ...]' LIMIT 10 — it creates HNSW indexes that turn sequential scans into fast approximate nearest neighbor lookups. Embedding-based search (semantic search, recommendation engines, RAG retrieval) benefits dramatically from HNSW indexing.

Without it: Vector similarity queries are proxied to PostgreSQL as normal. They work, but each query performs a sequential scan comparing the query vector against every row. The queries produce correct results, just slower than they need to be.

Install

CREATE EXTENSION IF NOT EXISTS vector;

Available on most managed providers (RDS, Supabase, Neon, Cloud SQL). No restart required.

pg_ivm

Auto-installed by GL

Instant materialized view refresh — every write to a base table immediately updates dependent matviews. Zero staleness.

This is the extension I would most encourage you to install, if your environment supports it. Standard materialized views in PostgreSQL are static snapshots — they must be explicitly refreshed to reflect new data. pg_ivm changes this entirely. It maintains materialized views via triggers on the base tables: every INSERT, UPDATE, or DELETE immediately updates the matview. Zero staleness.

A write to your orders table instantly updates any materialized view that depends on orders. Reads always return current data, with no rebuild delay and no refresh cycle to wait for.

Without it: Gold Lapel refreshes materialized views on a configurable polling cycle (default: every 60 seconds). When a write is detected, the affected matview is marked stale and subsequent reads route directly to PostgreSQL until the next refresh completes. Gold Lapel never serves stale data — it routes around it. This works reliably, but pg_ivm eliminates the staleness window entirely.

Install

-- 1. Install the pg_ivm package on your PostgreSQL server
-- 2. Add to postgresql.conf:
shared_preload_libraries = 'pg_ivm'
-- 3. Restart PostgreSQL
-- 4. Create the extension:
CREATE EXTENSION IF NOT EXISTS pg_ivm;

Third-party extension — not bundled with PostgreSQL. Requires both a package installation on the server and a shared_preload_libraries entry with a restart. Available on some managed providers; check your provider's extension list. If yours does not offer it, Gold Lapel's polling refresh handles the duty capably.

PostGIS

Manual install required

Geospatial queries — required for Gold Lapel's geoadd, georadius, and geodist methods.

PostGIS adds geographic object support to PostgreSQL — spatial data types, spatial indexes, and hundreds of functions for distance calculations, containment checks, and geometry operations. Gold Lapel's geospatial convenience methods (part of the Redis Replacement API) use PostGIS under the hood for ST_DWithin radius queries and ST_Distance calculations.

Unlike the other extensions on this page, PostGIS requires a system-level package installation before PostgreSQL can load it. geoadd() auto-creates the SQL extension, but the underlying library must already be present on the server.

Without it: All non-geospatial Gold Lapel features work normally. Only the geoadd, georadius, and geodist methods require PostGIS. If you are not using geospatial queries, you do not need this extension.

Install the system package

# Ubuntu/Debian
sudo apt-get install postgresql-16-postgis-3

# macOS (Homebrew)
brew install postgis

Create the extension in PostgreSQL

CREATE EXTENSION IF NOT EXISTS postgis;

PostGIS is a large extension with its own release cycle. Version availability varies by provider — most managed providers offer PostGIS, but check your provider's extension list. No shared_preload_libraries entry or restart required.

pllua

Manual install required

Server-side Lua scripting — required for Gold Lapel's script() method for atomic multi-step operations.

pllua embeds a Lua interpreter inside PostgreSQL, allowing you to execute Lua code directly on the database server. Gold Lapel's script() method uses pllua under the hood to run atomic multi-step operations — the Redis equivalent of EVAL / Lua scripting. This enables complex read-modify-write sequences that execute entirely within the database, without round-trips between your application and Postgres.

Like PostGIS, pllua requires a system-level package installation before PostgreSQL can load it. script() auto-creates the SQL extension, but the underlying library must already be present on the server.

Without it: All non-scripting Gold Lapel features work normally. Only the script() method requires pllua. PL/pgSQL functions still work via standard SQL — pllua is only needed for the Redis-style Lua scripting interface.

Install the system package

# Ubuntu/Debian
sudo apt install postgresql-16-pllua

Create the extension in PostgreSQL

CREATE EXTENSION IF NOT EXISTS pllua;

Not available on most managed providers (RDS, Neon, Supabase). Available on self-hosted PostgreSQL and future Gold Lapel Manor. No shared_preload_libraries entry or restart required.

Multiple shared_preload_libraries

If you are installing both pg_stat_statements and pg_ivm, combine them in a single shared_preload_libraries entry. One restart covers both:

shared_preload_libraries = 'pg_stat_statements, pg_ivm'

The order does not matter. PostgreSQL loads all listed libraries at startup regardless of sequence.

The dedicated Postgres Extensions page goes into considerably more detail — what Gold Lapel does with each extension, how it behaves without them, and what to expect at startup.

WAL Configuration Strongly Recommended

This is the single most important configuration for cache correctness. Gold Lapel strongly recommends setting wal_level = logical. Without it, cache invalidation relies solely on SQL parsing at the proxy layer — which covers most writes but cannot detect writes that bypass the proxy. With it, Gold Lapel monitors the write-ahead log directly, and every write to the database triggers cache invalidation regardless of source.

Setting wal_level to logical

Logical decoding requires wal_level = logical. This is the highest WAL level in PostgreSQL, and it includes all the information needed for Gold Lapel to see exactly which tables were modified by any transaction, regardless of where that transaction originated.

First, check your current setting:

SHOW wal_level;
Expected output
 wal_level
-----------
 logical
(1 row)

If your WAL level is replica (the default) or minimal, you will need to change it:

-- In postgresql.conf:
wal_level = logical

-- Then restart PostgreSQL:
sudo systemctl restart postgresql

This requires a PostgreSQL restart. Plan accordingly — on production systems, coordinate this with a maintenance window. The restart itself is fast; the configuration change is what requires planning.

Why this matters for cache correctness

WAL logical decoding reports every write (INSERT, UPDATE, DELETE, TRUNCATE) that happens in the database, including writes that Gold Lapel's proxy layer cannot see on its own:

  • Writes that bypass the proxy — migration scripts run directly against the database, admin psql sessions, cron jobs, pg_dump restores
  • Writes inside functions and stored procedures — a SELECT my_function() that internally performs an INSERT looks like a read at the SQL layer, but the WAL captures the underlying write
  • Writes from external tools — Flyway, Liquibase, Django manage.py migrations, Prisma migrate, or any other tool that connects directly to Postgres
  • Writes on a mesh peer that was temporarily offline — when the peer reconnects, the WAL ensures Gold Lapel sees every change that occurred during the disconnect

Without WAL logical decoding, a write that happens through any of these paths will not invalidate the cache, and stale data could be served until the affected matview's next refresh cycle.

What happens without logical WAL

Gold Lapel does not strictly require wal_level = logical. When logical decoding is unavailable, Gold Lapel falls back to SQL parsing at the proxy layer, which detects approximately 95% of writes. This covers all standard application traffic that flows through the proxy.

The proxy-layer parser cannot see:

  • Writes inside PL/pgSQL functions called via SELECT
  • Writes from connections that do not go through the proxy
  • Writes that happened while a mesh peer was disconnected

For applications where all writes flow through the proxy and you do not use functions that perform writes internally, the fallback mode works well. But if you run migrations directly against the database, have cron jobs or admin scripts that write data, or use stored procedures that modify tables — wal_level = logical is the only way to guarantee that every write triggers cache invalidation.

The question of which detection mode to choose warrants its own discussion — and has one, in the Write Detection guide.

NOTIFY write detection

When Gold Lapel has a management connection to the database, NOTIFY-based write detection starts automatically. There is nothing to pass — no --logical-decoding flag, no --notify-fallback option. If a management connection is available, Gold Lapel uses it. This provides reliable write detection even when wal_level = logical is not available in your environment.

If you prefer to disable this behavior — perhaps in a testing environment where write detection is unnecessary — pass --disable-notify at startup. Otherwise, NOTIFY detection is quietly taken care of on your behalf.

Permissions

Gold Lapel creates database objects on your behalf — materialized views, indexes, a metadata schema, and optionally extensions. The database user it connects as needs sufficient privileges to perform this work.

What Gold Lapel creates

  • _goldlapel schema — all Gold Lapel objects live in a dedicated schema, cleanly separated from your application's tables
  • Materialized views — pre-computed query results in _goldlapel.mv_<hash>
  • Indexes — targeted indexes on both base tables and matviews in _goldlapel.idx_<hash>
  • Metadata tables_goldlapel.meta and _goldlapel.index_meta for tracking what was created and why
  • ExtensionsCREATE EXTENSION for pg_trgm, pg_stat_statements, pg_ivm, fuzzystrmatch, and pgvector if not already present

The simplest and most reliable approach: make the Gold Lapel user the owner of the target database. This grants all necessary privileges without fine-grained permission management:

-- Option A: Make the GL user the database owner (recommended)
ALTER DATABASE mydb OWNER TO goldlapel;

-- Option B: Grant specific privileges
GRANT CREATE ON DATABASE mydb TO goldlapel;
GRANT CREATE ON SCHEMA public TO goldlapel;

If database ownership is not possible, the minimum privileges Gold Lapel needs are:

  • CREATE on the database — to create the _goldlapel schema
  • CREATE on the public schema — to create indexes on your tables
  • SELECT on application tables — to read data for matview creation and shadow verification
  • CREATE EXTENSION privilege — to install extensions if not already present (optional; you can install them manually)

Gold Lapel never modifies your application tables or data. It reads from them to build materialized views and creates its own objects in a separate schema. The goldlapel clean command removes everything Gold Lapel created, leaving your database exactly as it was.

Checking current privileges

Connect as the user Gold Lapel will use and run:

-- Check current user's privileges on the database
SELECT has_database_privilege(current_user, current_database(), 'CREATE');

-- Check schema creation privilege
SELECT has_schema_privilege(current_user, 'public', 'CREATE');

-- Check if the user owns the database (ideal)
SELECT d.datname, pg_catalog.pg_get_userbyid(d.datdba) AS owner
FROM pg_catalog.pg_database d
WHERE d.datname = current_database();

If any of these return f (false), you will need to grant the corresponding privilege before Gold Lapel can fully optimize. Gold Lapel still starts and proxies queries without these privileges — it simply cannot create the database objects that power its optimizations.

Provider-Specific Notes

Managed PostgreSQL providers each have their own mechanisms for configuring extensions, server parameters, and WAL settings. The essentials are the same everywhere — the details of how to apply them differ.

AWS RDS / Aurora

RDS manages PostgreSQL configuration through parameter groups. You cannot edit postgresql.conf directly — instead, you modify the parameter group attached to your instance.

-- In your RDS parameter group:
shared_preload_libraries = 'pg_stat_statements'
rds.logical_replication = 1

-- Then reboot the RDS instance for changes to take effect.
-- After reboot, connect and create the extensions:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
CREATE EXTENSION IF NOT EXISTS vector;

Extensions: pg_stat_statements, pg_trgm, fuzzystrmatch, and pgvector are available on all RDS PostgreSQL versions. pg_ivm is not available on RDS at this time.

shared_preload_libraries: Set in your custom parameter group. The default parameter group is read-only — create a custom one if you have not already. After changing this value, reboot the instance.

wal_level: Set rds.logical_replication = 1 in the parameter group. This sets wal_level = logical and enables the required replication slots. Requires a reboot.

Permissions: The master user has sufficient privileges. If using a separate Gold Lapel user, grant it rds_superuser role or specific CREATE privileges on the target database.

Supabase

Supabase provides a web dashboard for managing extensions and has several configuration defaults that work in Gold Lapel's favor.

-- Extensions dashboard: Database → Extensions
-- Or via SQL:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
CREATE EXTENSION IF NOT EXISTS vector;

Extensions: pg_stat_statements, pg_trgm, fuzzystrmatch, and pgvector are available via the Extensions dashboard or SQL. pg_ivm is not currently available on Supabase.

wal_level: Already set to logical by default on Supabase. No action needed — Gold Lapel's logical decoding works out of the box.

Permissions: The postgres role has sufficient privileges. If creating a dedicated Gold Lapel user, grant it the privileges described above.

Connection pooling: Supabase uses Supavisor for connection pooling. Gold Lapel auto-detects Supavisor by hostname and disables its own pool to avoid stacking poolers. Use the direct connection string (port 5432), not the pooled connection string (port 6543), for Gold Lapel's upstream.

Neon

Neon's serverless PostgreSQL has a few considerations around connection limits and auto-suspend that are worth knowing about.

-- Extensions available via SQL:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
CREATE EXTENSION IF NOT EXISTS vector;

Extensions: pg_stat_statements, pg_trgm, fuzzystrmatch, and pgvector are available. pg_ivm is not currently available on Neon.

wal_level: Set to logical by default on Neon. No action needed.

Auto-suspend: The default mgmt_idle_timeout_secs = 60 disconnects Gold Lapel's management connection when idle, allowing Neon's compute to scale to zero. No special configuration required. If you prefer an always-on connection, set mgmt_idle_timeout_secs = 0.

Connection limits: Neon's connection limits vary by plan — Free (20), Launch (100), Scale (500+). Gold Lapel uses one management connection plus one connection per pool slot. If you are on the Free plan, consider setting --pool-size to a conservative value.

Connection pooling: Neon provides built-in connection pooling. Gold Lapel auto-detects it and adjusts accordingly. Use the direct endpoint for the upstream connection.

Heroku Postgres

Heroku's extension availability depends on your plan tier.

-- Standard and Premium plans:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
CREATE EXTENSION IF NOT EXISTS vector;

Extensions: pg_stat_statements is available on Standard and Premium plans (not Essential). pg_trgm, fuzzystrmatch, and pgvector are available on all plans. pg_ivm is not available on Heroku.

shared_preload_libraries: Managed by Heroku — pg_stat_statements is pre-loaded on Standard and Premium plans. You only need to run CREATE EXTENSION.

wal_level: Standard and Premium plans support logical replication. Essential plans do not. Check with SHOW wal_level; to confirm your plan's setting.

Permissions: The default database credential has sufficient privileges for Gold Lapel's needs.

Google Cloud SQL

Cloud SQL uses database flags for server configuration, accessible via the Cloud Console, gcloud CLI, or Terraform.

-- In Cloud SQL flags:
-- cloudsql.enable_pg_stat_statements = on
-- Then:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
CREATE EXTENSION IF NOT EXISTS vector;

Extensions: pg_stat_statements, pg_trgm, fuzzystrmatch, and pgvector are available. Enable pg_stat_statements via the cloudsql.enable_pg_stat_statements flag. pg_ivm is not currently available on Cloud SQL.

wal_level: Set the cloudsql.logical_decoding flag to on in your instance's database flags. This enables wal_level = logical. Requires an instance restart.

Permissions: The default postgres user has sufficient privileges. Cloud SQL does not grant true superuser access, but the cloudsql.pg_admin role provides everything Gold Lapel needs.

Verification Checklist

Once you have made your changes — or if you simply want to confirm the current state of your database — this single block checks everything. Connect as the user Gold Lapel will use and run:

-- 1. Check installed extensions
SELECT extname, extversion
FROM pg_extension
WHERE extname IN ('pg_trgm', 'pg_ivm', 'pg_stat_statements', 'fuzzystrmatch', 'vector', 'postgis', 'pllua');

-- 2. Check WAL level
SHOW wal_level;

-- 3. Check CREATE privilege on the database
SELECT has_database_privilege(current_user, current_database(), 'CREATE');

-- 4. Check if shared_preload_libraries includes what you need
SHOW shared_preload_libraries;

-- 5. Confirm the _goldlapel schema can be created
SELECT has_schema_privilege(current_user, 'public', 'CREATE');

On a fully configured database, the results will look like this:

Ideal output
-- Extensions
 extname            | extversion
--------------------+-----------
 pg_stat_statements | 1.10
 pg_trgm            | 1.6
 pg_ivm             | 1.9
 fuzzystrmatch      | 1.2
 vector             | 0.7
 postgis            | 3.4
 pllua              | 2.0

-- WAL level
 wal_level
-----------
 logical

-- CREATE privilege
 has_database_privilege
------------------------
 t

-- Shared preload libraries
 shared_preload_libraries
--------------------------
 pg_stat_statements, pg_ivm

-- Schema privilege
 has_schema_privilege
----------------------
 t

Not everything needs to show t or be present for Gold Lapel to work. Here is what each result means:

  • Extensions present — Gold Lapel uses whatever is available. Missing extensions mean specific features are disabled, not that Gold Lapel cannot run.
  • wal_level = logical — strongly recommended for cache correctness. Enables Gold Lapel to detect every write to the database regardless of source. If replica or minimal, Gold Lapel falls back to proxy-layer SQL parsing, which cannot see writes that bypass the proxy.
  • CREATE privilege = t — required for Gold Lapel to create its schema, matviews, and indexes. If f, Gold Lapel still proxies and observes queries but cannot create optimization objects.
  • shared_preload_libraries — confirms that extensions requiring preload (pg_stat_statements, pg_ivm) are loaded. If an extension is not listed here, CREATE EXTENSION will fail for it.
  • Schema privilege = t — confirms Gold Lapel can create indexes on tables in the public schema.

Gold Lapel also runs these checks itself at startup and logs the results clearly — what is available, what is missing, and what to do about it. If you prefer to let Gold Lapel tell you what it needs, simply start it and read the logs.