electric-postgres-security
This skill builds on electric-proxy-auth. Read it first for proxy security patterns.
Electric — Postgres Security Checklist
Run through each section before deploying Electric to production.
User Permission Checks
Check: Electric user has REPLICATION role
Expected:
SELECT rolreplication FROM pg_roles WHERE rolname = 'electric_user';
-- Should return: true
Fail condition: rolreplication = false or user does not exist.
Fix: ALTER ROLE electric_user WITH REPLICATION;
Check: Electric user has SELECT on synced tables
Expected:
SELECT has_table_privilege('electric_user', 'todos', 'SELECT');
-- Should return: true
Fail condition: Returns false.
Fix: GRANT SELECT ON todos TO electric_user; or GRANT SELECT ON ALL TABLES IN SCHEMA public TO electric_user;
Check: Electric user has CREATE on database
Expected:
SELECT has_database_privilege('electric_user', current_database(), 'CREATE');
-- Should return: true (unless using manual publishing mode)
Fail condition: Returns false and not using ELECTRIC_MANUAL_TABLE_PUBLISHING=true.
Fix: GRANT CREATE ON DATABASE mydb TO electric_user;
Table Configuration Checks
Check: REPLICA IDENTITY FULL on all synced tables
Expected:
SELECT relname, relreplident
FROM pg_class
WHERE relname IN ('todos', 'users')
AND relreplident = 'f'; -- 'f' = FULL
Fail condition: relreplident is 'd' (default) or 'n' (nothing).
Fix: ALTER TABLE todos REPLICA IDENTITY FULL;
Check: Tables are in the Electric publication
Expected:
SELECT tablename FROM pg_publication_tables
WHERE pubname = 'electric_publication_default';
Fail condition: Synced tables missing from the list.
Fix (manual mode): ALTER PUBLICATION electric_publication_default ADD TABLE todos;
Connection Checks
Check: DATABASE_URL uses direct connection (not pooler)
Expected:
DATABASE_URL=postgres://user:pass@db-host:5432/mydb
Fail condition: URL points to a connection pooler (e.g., PgBouncer on port 6432, Supabase pooler).
Fix: Use direct Postgres connection for DATABASE_URL. Set ELECTRIC_POOLED_DATABASE_URL separately for pooled queries.
Check: wal_level is set to logical
Expected:
SHOW wal_level;
-- Should return: logical
Fail condition: Returns replica or minimal.
Fix: Set wal_level = logical in postgresql.conf and restart Postgres.
Common Security Mistakes
CRITICAL Using connection pooler for DATABASE_URL
Wrong:
DATABASE_URL=postgres://user:pass@pooler.example.com:6432/mydb
Correct:
DATABASE_URL=postgres://user:pass@db.example.com:5432/mydb
ELECTRIC_POOLED_DATABASE_URL=postgres://user:pass@pooler.example.com:6432/mydb
Connection poolers (except PgBouncer 1.23+) do not support logical replication. Electric must connect directly to Postgres for its replication slot.
Source: website/docs/guides/deployment.md:91
HIGH Missing REPLICA IDENTITY FULL on tables
Wrong:
CREATE TABLE todos (id UUID PRIMARY KEY, text TEXT);
-- Replica identity defaults to 'default' (PK only)
Correct:
CREATE TABLE todos (id UUID PRIMARY KEY, text TEXT);
ALTER TABLE todos REPLICA IDENTITY FULL;
Without REPLICA IDENTITY FULL, Electric cannot stream the full row on updates and deletes. Updates may be missing non-PK columns.
Source: website/docs/guides/troubleshooting.md:373
HIGH Electric user without REPLICATION role
Wrong:
CREATE USER electric_user WITH PASSWORD 'secret';
Correct:
CREATE USER electric_user WITH PASSWORD 'secret' REPLICATION;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO electric_user;
Electric uses logical replication and requires the REPLICATION role on the database user.
Source: website/docs/guides/postgres-permissions.md
Pre-Deploy Summary
- Electric user has
REPLICATIONrole - Electric user has
SELECTon all synced tables - Electric user has
CREATEon database (or manual publishing configured) - All synced tables have
REPLICA IDENTITY FULL - All synced tables are in the Electric publication
-
DATABASE_URLuses direct Postgres connection (not pooler) -
wal_level = logicalin Postgres config -
ELECTRIC_SECRETis set (not usingELECTRIC_INSECURE=true) - Secrets are injected server-side only (never in client bundle)
See also: electric-proxy-auth/SKILL.md — Proxy injects secrets that Postgres security enforces. See also: electric-deployment/SKILL.md — Deployment requires correct Postgres configuration.
Version
Targets Electric sync service v1.x.