Database Maintenance
Rack Gateway uses PostgreSQL for persistent storage. This guide covers database maintenance tasks including migrations, backups, and troubleshooting.
Database Commands
Section titled “Database Commands”The rack-gateway-api binary includes maintenance subcommands for database operations.
Apply Migrations
Section titled “Apply Migrations”Apply pending database migrations:
rack-gateway-api migrateThis command:
- Connects using the first of
RGW_DATABASE_URL,GATEWAY_DATABASE_URLandDATABASE_URLthat is set, or else thePG*environment variables - Applies the migrations built into the binary (from
internal/gateway/db/migrations/), each in its own transaction - Uses
schema_migrationstable to track applied migrations - Is safe to run repeatedly (idempotent)
Run it as the admin database user: on Convox, convox run admin ./rack-gateway-api migrate -a rack-gateway (the
admin service sets RGW_DATABASE_URL to ADMIN_DATABASE_URL). The app user can’t change the schema.
Reset Database
Section titled “Reset Database”Reset the database to a clean state (development only):
# Required safety guardsexport RESET_RACK_GATEWAY_DATABASE=DELETE_ALL_DATAexport DEV_MODE=true # development (testbypass) builds only
rack-gateway-api reset-dbIt drops every table and re-runs the migrations. The gateway creates the seed users again when it next starts.
Safety guards:
RESET_RACK_GATEWAY_DATABASE=DELETE_ALL_DATA- Must be set exactly- Database must be marked as development, OR set
DISABLE_DATABASE_ENVIRONMENT_CHECK=1 - For a database not yet marked,
DEV_MODE=true(development builds only) orDISABLE_DATABASE_ENVIRONMENT_CHECK=1
Verify the Audit Log
Section titled “Verify the Audit Log”Check the audit log’s hash chain and its S3 anchors (read-only; exits non-zero on any integrity failure):
rack-gateway-api verify-audit [--from N] [--to N] [--hash-v2-from N] [--max-anchor-age 3h] [--no-anchors]It needs AUDIT_HMAC_SECRET, and AUDIT_HASH_V2_FROM on a gateway upgraded from a version before hash v2. See
Verification for the permissions it needs and what it
checks. The gateway also runs this check every hour and alerts when it fails.
Check Migration Status
Section titled “Check Migration Status”Verify which migrations have been applied:
# Connect to database and checkpsql $DATABASE_URL -c "SELECT * FROM schema_migrations ORDER BY version;"Backup Procedures
Section titled “Backup Procedures”Automated Backups (AWS RDS)
Section titled “Automated Backups (AWS RDS)”If using AWS RDS, configure automated backups:
# Terraform configurationresource "aws_db_instance" "gateway" { # Automated backups backup_retention_period = 30 backup_window = "03:00-04:00"
# Point-in-time recovery copy_tags_to_snapshot = true}Long-Term Retention (AWS Backup)
Section titled “Long-Term Retention (AWS Backup)”RDS automated backups keep at most 35 days. For longer retention (for example 12 monthly and 7 yearly backups), add the instance to an AWS Backup plan, one per environment:
resource "aws_backup_vault" "gateway" { name = "rack-gateway-backups" kms_key_arn = aws_kms_key.backup.arn}
resource "aws_backup_plan" "gateway" { name = "rack-gateway"
rule { rule_name = "daily" target_vault_name = aws_backup_vault.gateway.name schedule = "cron(0 3 * * ? *)" lifecycle { delete_after = 7 } }
rule { rule_name = "monthly" target_vault_name = aws_backup_vault.gateway.name schedule = "cron(0 5 1 * ? *)" lifecycle { delete_after = 365 } }
rule { rule_name = "yearly" target_vault_name = aws_backup_vault.gateway.name schedule = "cron(0 6 1 1 ? *)" lifecycle { delete_after = 2555 } }}
resource "aws_backup_selection" "gateway_rds" { name = "rack-gateway-rds" plan_id = aws_backup_plan.gateway.id iam_role_arn = aws_iam_role.backup.arn # a role AWS Backup can assume, with its RDS backup policy resources = [aws_db_instance.gateway.arn]}Set the schedules and retention to your policy. The database is the only copy of the full audit log (the S3 anchors hold hashes), so its backups are your audit log retention.
Manual Backup
Section titled “Manual Backup”Create a manual backup using pg_dump with the admin connection (the app user can’t read every table):
# Full database backuppg_dump "$ADMIN_DATABASE_URL" > rack_gateway_backup_$(date +%Y%m%d).sql
# Compressed backuppg_dump "$ADMIN_DATABASE_URL" | gzip > rack_gateway_backup_$(date +%Y%m%d).sql.gzRestore from Backup
Section titled “Restore from Backup”Prefer the RDS point-in-time restore to just before the problem. A restore to an earlier point loses the audit
events written since, and the S3 anchors written after that point still name them: verify-audit then fails with
anchored event N is missing from the database. Keep that report with your incident record.
-
Stop the gateway service
Terminal window convox scale gateway --count 0 -a rack-gateway -
Restore the database
Restore the RDS snapshot, or load a dump as the admin user into a database with the audit roles already created (
scripts/audit-roles.sql, see Database Users and Audit Roles):Terminal window psql "$ADMIN_DATABASE_URL" < rack_gateway_backup_20240115.sql -
Run any pending migrations
Terminal window convox run admin ./rack-gateway-api migrate -a rack-gateway -
Restart the service and verify the audit log
Terminal window convox scale gateway --count 1 -a rack-gatewayconvox run admin ./rack-gateway-api verify-audit -a rack-gateway
Schema Overview
Section titled “Schema Overview”The database contains these main tables:
| Table | Purpose |
|---|---|
users | User accounts from OAuth |
user_sessions | Active user sessions |
api_tokens | API tokens for CI/CD |
mfa_methods | User MFA enrollments |
audit.audit_event | Immutable audit log entries |
mfa_backup_codes | MFA backup codes |
trusted_devices | Trusted devices (skip MFA at web login) |
settings | Gateway configuration |
deploy_approval_requests | Deploy approval requests |
river_job | Background jobs (emails, Slack, CI, anchors) |
Key Relationships
Section titled “Key Relationships”Maintenance Tasks
Section titled “Maintenance Tasks”Cleanup Old Sessions
Section titled “Cleanup Old Sessions”Expired and revoked sessions stay in the table (nothing prunes them). To clear old ones, as the admin user:
-- Sessions that ended more than 30 days agoDELETE FROM user_sessionsWHERE (revoked_at IS NOT NULL AND revoked_at < NOW() - INTERVAL '30 days') OR expires_at < NOW() - INTERVAL '30 days';Don’t delete by created_at: a CLI session stays valid as long as it’s used every 90 days.
Audit Log Growth
Section titled “Audit Log Growth”Audit events are never deleted. The database refuses UPDATE and DELETE on audit.audit_event, and the hash
chain needs every event to verify. Plan storage for the log to grow (see
Database Setup):
-- Audit log volume per daySELECT COUNT(*), DATE(timestamp) AS dayFROM audit.audit_eventGROUP BY DATE(timestamp)ORDER BY day DESCLIMIT 10;Vacuum and Analyze
Section titled “Vacuum and Analyze”Regular maintenance improves performance:
-- Analyze query statisticsANALYZE;
-- Reclaim space from deleted rowsVACUUM ANALYZE;For RDS, this happens automatically. For self-managed PostgreSQL, schedule regular maintenance.
Connection Management
Section titled “Connection Management”Connection Pool Settings
Section titled “Connection Pool Settings”Configure connection pooling (the defaults are shown; see Connection Pool for sizing):
DB_MAX_OPEN_CONNS=10 # Maximum connections per pool (each process has two)DB_MAX_IDLE_CONNS=3 # Idle connections to keep warm, per poolDB_CONN_MAX_LIFETIME=30m # Connection lifetimeDB_CONN_MAX_IDLE_TIME=10m # Idle timeoutMonitoring Connections
Section titled “Monitoring Connections”-- Current active connectionsSELECT count(*) FROM pg_stat_activityWHERE datname = 'rack_gateway';
-- Connection detailsSELECT usename, application_name, client_addr, stateFROM pg_stat_activityWHERE datname = 'rack_gateway';Troubleshooting
Section titled “Troubleshooting”Migration Failures
Section titled “Migration Failures”If a migration fails:
- Check the error message for the specific issue
- Fix the underlying problem (permissions, constraints, etc.)
- Re-run
rack-gateway-api migrate
Each migration runs in a transaction with its schema_migrations row, so a failed migration leaves nothing
behind and is retried on the next run.
Connection Issues
Section titled “Connection Issues”If the gateway can’t connect to the database:
# Test connectivitypsql $DATABASE_URL -c "SELECT 1;"
# Check if host is reachablenc -zv $PGHOST $PGPORT
# Verify SSL settingspsql "$DATABASE_URL?sslmode=require" -c "SELECT 1;"Slow Queries
Section titled “Slow Queries”Identify slow queries:
-- Enable query logging (if not already)ALTER SYSTEM SET log_min_duration_statement = '1000'; -- Log queries > 1s
-- Check for long-running queriesSELECT pid, now() - pg_stat_activity.query_start AS duration, queryFROM pg_stat_activityWHERE state = 'active'ORDER BY duration DESC;Disk Space
Section titled “Disk Space”Check database size:
SELECT pg_size_pretty(pg_database_size('rack_gateway'));
-- Size by tableSELECT relname, pg_size_pretty(pg_total_relation_size(relid))FROM pg_catalog.pg_statio_user_tablesORDER BY pg_total_relation_size(relid) DESC;Docker Development
Section titled “Docker Development”When using the Docker development stack:
Migrate
Section titled “Migrate”task docker:db:migratetask docker:db:resetConnect to Database
Section titled “Connect to Database”docker exec -it rack-gateway-postgres-1 psql -U postgres -d gateway_devBest Practices
Section titled “Best Practices”- Always run migrations before promoting a release - Include in deployment scripts
- Use automated backups - Configure RDS backup retention
- Monitor connection usage - Prevent connection exhaustion
- Keep audit logs - They can’t be deleted; plan storage for them
- Test restore procedures - Verify backups actually work, and run
verify-auditon the restored copy
Further Reading
Section titled “Further Reading”- AWS Infrastructure - RDS configuration
- Production Checklist - Pre-deployment verification
- Troubleshooting - Common issues