
Postgres
Run logical and physical PostgreSQL backups, WAL archiving, and tested recovery including PITR for a production database you own.
Overview
Postgres (backup and recovery) is an agent skill for the Operate phase that guides logical pg_dump backups, pg_basebackup physical copies, WAL archiving, and PITR recovery for PostgreSQL.
Install
npx skills add https://github.com/planetscale/database-skills --skill postgresWhat is this skill?
- Logical backups with pg_dump formats (-Fp, -Fc, -Fd with parallel -j) and selective pg_restore
- Physical backups via pg_basebackup and PGDATA copy constraints (version, platform, endianness)
- PITR workflow: base backup plus continuous WAL archiving and archive_command safety rules
- Recovery testing emphasis: backups are useless until a successful restore is proven
- RPO framing: daily dumps vs minute-level PITR when WAL archiving is configured
- Documents four pg_dump output formats: -Fp, -Fc, -Fd, -Ft
- Typical logical-backup RPO of 24h without continuous WAL archiving
- PITR can reduce RPO to minutes when base backup plus WAL archive is configured
Adoption & trust: 4.7k installs on skills.sh; 484 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You run PostgreSQL in production but do not have a tested backup and restore plan that matches your acceptable data loss window.
Who is it for?
Solo builders shipping SaaS or APIs on self-managed Postgres who need durable backups and rehearsed recovery steps.
Skip if: Greenfield schema design-only tasks or teams on fully managed backup SLAs with no hands-on restore responsibility.
When should I use this skill?
You need PostgreSQL backup formats, physical base backups, WAL archiving, or PITR recovery strategies in production.
What do I get? / Deliverables
You choose logical vs physical backup tools, configure WAL archiving for PITR when needed, and restore with documented pg_restore or base-backup procedures after validating recovery.
- Backup runbook covering logical vs physical strategy
- PITR and restore checklist with tested recovery steps
Recommended Skills
Journey fit
Backup and recovery is production operations work that belongs on the Operate shelf once the app is live. Infra subphase covers database durability, archives, and restore paths rather than schema design or query tuning.
How it compares
Operational runbook for Postgres durability, not a general SQL query or migration authoring skill.
Common Questions / FAQ
Who is postgres backup and recovery for?
Indie operators and small teams running PostgreSQL who must own backup format choices, archiving, and restore drills themselves.
When should I use postgres backup and recovery?
Use it in Operate when provisioning backups, tuning archive_command, planning PITR, or rehearsing pg_dump and pg_basebackup restores before an incident.
Is postgres backup and recovery safe to install?
Review the Security Audits panel on this Prism page and confirm any shell or filesystem access your agent needs matches your backup storage policy.
SKILL.md
READMESKILL.md - Postgres
# Backup and Recovery **FUNDAMENTAL RULE: Backups are useless until you've successfully tested recovery.** ## Logical Backups (pg_dump) Exports as SQL or custom format; portable across PG versions and architectures. Formats: `-Fp` (plain SQL), `-Fc` (custom compressed, selective restore), `-Fd` (directory, parallel with `-j`), `-Ft` (tar, avoid). Use `-Fd -j 4` for large DBs. Restore: `pg_restore -d dbname file.dump`; add `-j` for parallel restore. Selective table restore: `pg_restore -t tablename`. Slow for large DBs; RPO = backup frequency (typically 24h). ## Physical Backups (pg_basebackup) Copies raw PGDATA; same major version and platform required; cross-architecture works if same endianness (e.g., x86_64 ↔ ARM64). Faster for large clusters; includes all databases. Flags: `-Ft -z -P` for compressed tar with progress. Manual alternative: `pg_backup_start()` → copy PGDATA → `pg_backup_stop()` (complex; must write returned `backup_label`). ## PITR (Point-in-Time Recovery) Requires base backup + continuous WAL archiving. Restores to any timestamp, transaction, or named restore point. Without PITR: restore only to backup time (potentially lose hours). With PITR: RPO = minutes. `archive_command` must return 0 ONLY when file is safely stored—premature 0 = data loss risk. `wal_level` must be `replica` or `logical` (not `minimal`). ## WAL Archiving `archive_mode=on`, `archive_command='test ! -f /archive/%f && cp %p /archive/%f'`. **Test archive command as postgres user** (not root) since permission issues are common. Monitor `pg_stat_archiver` for `failed_count`, `last_archived_time`. Archive failures prevent WAL recycling → disk fills. ## Tool Comparison | Tool | Use case | |------|----------| | pg_dump | Small DBs, migrations, selective restore | | pg_basebackup | Basic PITR, built-in | | pgBackRest | Production—parallel, incremental, S3/GCS/Azure, retention | | Barman | Enterprise PITR, retention policies | | WAL-G | Cloud-native, S3/GCS/Azure | ## RPO/RTO Logical only: RPO = backup interval (hours); RTO = hours. PITR: RPO = minutes; RTO = hours. Synchronous replication: RPO = 0; RTO = seconds to minutes (failover). ## Operational Rules - Verify integrity with `pg_verifybackup` (PG 13+) - Test recovery / PITR regularly - Take backups from standby to avoid impacting primary - Retention: 7 daily, 4 weekly, 12 monthly - Monitor archive growth and backup age - **Never assume backups work without testing** --- title: Index Optimization Queries description: Index audit queries tags: postgres, indexes, unused-indexes, duplicate-indexes, invalid-indexes, bloat, HOT, write-amplification, planner-tuning, optimization --- # Index Optimization ## Identify Unused Indexes Query to find unused indexes: ```sql -- indexes with 0 scans (check pg_stat_reset / pg_postmaster_start_time first) SELECT s.schemaname, s.relname AS table_name, s.indexrelname AS index_name, pg_size_pretty(pg_relation_size(s.indexrelid)) AS index_size FROM pg_catalog.pg_stat_user_indexes s JOIN pg_catalog.pg_index i ON s.indexrelid = i.indexrelid WHERE s.idx_scan = 0 AND 0 <> ALL (i.indkey) -- exclude expression indexes AND NOT i.indisunique -- exclude UNIQUE indexes AND NOT EXISTS ( -- exclude constraint-backing indexes SELECT 1 FROM pg_catalog.pg_constraint c WHERE c.conindid = s.indexrelid ) ORDER BY pg_relation_size(s.indexrelid) DESC; ``` ## Identify Duplicate Indexes Indexes with identical definitions (after normalizing names) on the same table are duplicates: ```sql SELECT schemaname || '.' || tablename AS table, array_agg(indexname) AS duplicate_indexes, pg_size_pretty(sum(pg_relation_size((schemaname || '.' || indexname)::regclass))) AS total_size FROM pg_