Database Migrations & Pooling
Configure PostgreSQL 16+, tune pgxpool connection limits, and execute automated database migrations.
ScanDrix requires PostgreSQL 16+ with Row-Level Security (RLS) enabled. The Go backend uses pgx/v5 with an internal pool manager (pgxpool) optimized for multi-tenant isolation and high-concurrency connection handling.
Connection pool configuration
Tune PostgreSQL pool settings via environment variables in production:
| Variable | Default | Recommended Production | Description |
|---|---|---|---|
DATABASE_URL | — | postgres://user:pass@host:5432/scandrix?sslmode=require | Standard PostgreSQL connection URI. |
DB_MAX_CONNS | 25 | 50 - 100 | Maximum open connections per API replica. |
DB_MIN_CONNS | 2 | 10 | Minimum idle connections kept warm. |
DB_MAX_CONN_LIFETIME | 1h | 1h | Maximum time a connection may be reused. |
DB_MAX_CONN_IDLE_TIME | 15m | 15m | Maximum time an idle connection remains alive. |
// Sample internal pool configuration from ScanDrix/internal/database/db.go
config.MaxConns = 50
config.MinConns = 10
config.MaxConnLifetime = 1 * time.Hour
config.MaxConnIdleTime = 15 * time.Minute
config.ConnConfig.DefaultQueryExecMode = pgx.QueryExecModeSimpleProtocol
Running database migrations
ScanDrix uses golang-migrate/migrate for versioned, atomic schema migrations located in migrations/:
# Apply all pending migrations forward
scandrix db migrate up
# Check current migration schema version
scandrix db version
# Roll back the single most recent migration (if needed)
scandrix db migrate down 1
Standalone Docker migration runner
In automated CI/CD pipelines, run migrations as a pre-deploy Kubernetes Job:
apiVersion: batch/v1
kind: Job
metadata:
name: scandrix-migration-job
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrator
image: ghcr.io/scandrix/scandrix:latest
command: ["/bin/scandrix", "db", "migrate", "up"]
envFrom:
- secretRef:
name: scandrix-db-credentials
Row-Level Security (RLS) verification
ScanDrix enforces tenant isolation at the database engine level. Every tenant-scoped query runs inside a transaction that sets:
SET LOCAL app.current_tenant_id = 'org_abc123';
Any attempt by a query to read or write rows belonging to another organization ID is rejected by PostgreSQL with an RLS policy violation error.