TL;DR — Resumen Rápido
Estrategias de backup en PostgreSQL: pg_dump, pg_dumpall, pg_basebackup, archivado WAL, PITR y scripts automatizados con rotación y subida a S3.
PostgreSQL es la columna vertebral de innumerables aplicaciones en producción — perder datos sin un plan de restauración probado es catastrófico. Esta guía cubre todas las principales estrategias de backup y restauración en PostgreSQL: backups lógicos con pg_dump y pg_dumpall, backups físicos con pg_basebackup, archivado WAL continuo para recuperación punto en tiempo (PITR), scripts automatizados, cifrado y subida a la nube.
Requisitos Previos
- PostgreSQL 14 o superior (los comandos funcionan desde la versión 12).
- El usuario
postgreso un rol con privilegiospg_read_all_data/SUPERUSER. - Espacio en disco suficiente (al menos 1,5× el tamaño de sus bases de datos).
aws-cliorcloneinstalado si usa almacenamiento en la nube.gpginstalado para backups cifrados.
Backups Lógicos con pg_dump
pg_dump exporta una única base de datos a SQL o a un formato binario personalizado. Es el método de backup más portable.
Backup Completo de la Base de Datos
# Formato personalizado (recomendado) — comprimido, soporta restauración selectiva
pg_dump -U postgres -Fc -d midb -f /backup/midb_$(date +%Y%m%d).pgdump
# SQL plano — legible pero más grande, restaurar con psql
pg_dump -U postgres -Fp -d midb -f /backup/midb_$(date +%Y%m%d).sql
Backup de Una Sola Tabla
pg_dump -U postgres -Fc -d midb -t public.pedidos -f /backup/pedidos_$(date +%Y%m%d).pgdump
Formato Personalizado vs SQL Plano
| Característica | Formato Personalizado (-Fc) | SQL Plano (-Fp) |
|---|---|---|
| Compresión | Integrada (zlib) | No (use gzip aparte) |
| Restauración selectiva | Sí (-t, -n, -T) | No |
| Restauración paralela | Sí (pg_restore -j) | No |
| Legible por humanos | No | Sí |
| Herramienta de restauración | pg_restore | psql |
Use formato personalizado para backups en producción.
Backups de Clúster con pg_dumpall
pg_dumpall respalda el clúster PostgreSQL completo: todas las bases, roles y tablespaces.
# Backup completo del clúster
pg_dumpall -U postgres -f /backup/cluster_$(date +%Y%m%d).sql
# Solo roles y tablespaces (sin datos)
pg_dumpall -U postgres --globals-only -f /backup/globals_$(date +%Y%m%d).sql
Restauración:
psql -U postgres -f /backup/cluster_20260322.sql
Backups Físicos con pg_basebackup
pg_basebackup crea una copia binaria del directorio de datos de PostgreSQL — la base para PITR y servidores standby.
pg_basebackup -U replicator -h localhost -D /backup/base \
-Ft -z -P --wal-method=stream
Requisitos:
- Rol con privilegio
REPLICATION:CREATE ROLE replicator REPLICATION LOGIN PASSWORD '...'; max_wal_senders >= 2enpostgresql.conf.- Entrada en
pg_hba.conf:host replication replicator 127.0.0.1/32 md5
Archivado WAL y Recuperación Punto en Tiempo
El archivado WAL combinado con un backup base permite PITR — restaurar a cualquier momento pasado.
Habilitar el Archivado WAL
Edite /etc/postgresql/16/main/postgresql.conf:
wal_level = replica
archive_mode = on
archive_command = 'cp %p /backup/wal_archive/%f'
archive_timeout = 300
Para S3:
archive_command = 'aws s3 cp %p s3://mis-backups-pg/wal/%f'
Recargue PostgreSQL:
sudo systemctl reload postgresql
Verificar que el Archivado Funciona
SELECT last_archived_wal, last_archived_time, last_failed_wal
FROM pg_stat_archiver;
Recuperación a un Punto Específico
- Detenga PostgreSQL y reemplace el directorio de datos con el backup base.
- Cree
recovery.signalen el directorio de datos (PostgreSQL 12+). - Configure los parámetros de recuperación en
postgresql.conf:
restore_command = 'cp /backup/wal_archive/%f %p'
recovery_target_time = '2026-03-22 14:30:00'
recovery_target_action = 'promote'
- Inicie PostgreSQL — reproducirá el WAL hasta el momento especificado.
Restauración desde Backups
Restaurar con pg_restore (Formato Personalizado)
createdb -U postgres midb_restaurada
pg_restore -U postgres -d midb_restaurada /backup/midb_20260322.pgdump
# Restauración paralela (mucho más rápida en servidores multi-núcleo)
pg_restore -U postgres -j 4 -d midb_restaurada /backup/midb_20260322.pgdump
# Restaurar solo una tabla
pg_restore -U postgres -d midb_restaurada -t public.pedidos /backup/midb_20260322.pgdump
Restaurar con psql (SQL Plano)
psql -U postgres -d midb_restaurada -f /backup/midb_20260322.sql
Ver el Contenido de un Backup
pg_restore --list /backup/midb_20260322.pgdump | grep TABLE
Scripts de Backup Automatizado
Script Diario con Rotación
#!/bin/bash
# /usr/local/bin/pg-backup.sh
set -euo pipefail
BACKUP_DIR="/backup/postgresql"
DIAS_A_CONSERVAR=7
PG_USER="postgres"
FECHA=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR/daily"
for DB in $(psql -U "$PG_USER" -t -c "SELECT datname FROM pg_database WHERE datistemplate = false AND datname != 'postgres'"); do
DB=$(echo "$DB" | xargs)
pg_dump -U "$PG_USER" -Fc -d "$DB" \
-f "$BACKUP_DIR/daily/${DB}_${FECHA}.pgdump"
done
pg_dumpall -U "$PG_USER" --globals-only \
-f "$BACKUP_DIR/daily/globals_${FECHA}.sql"
find "$BACKUP_DIR/daily" -type f -mtime +"$DIAS_A_CONSERVAR" -delete
0 2 * * * /usr/local/bin/pg-backup.sh >> /var/log/pg-backup.log 2>&1
Seguridad del Backup — Compresión, Cifrado y S3
Compresión con Gzip
pg_dump -U postgres -Fp -d midb | gzip > /backup/midb_$(date +%Y%m%d).sql.gz
gunzip -c /backup/midb_20260322.sql.gz | psql -U postgres -d midb_restaurada
Cifrado con GPG
pg_dump -U postgres -Fc -d midb | \
gpg --encrypt --recipient admin@ejemplo.com \
-o /backup/midb_$(date +%Y%m%d).pgdump.gpg
gpg --decrypt /backup/midb_20260322.pgdump.gpg | \
pg_restore -U postgres -d midb_restaurada
Subida a S3
aws s3 cp /backup/midb_20260322.pgdump \
s3://mis-backups-pg/daily/ --storage-class STANDARD_IA
aws s3 sync /backup/postgresql/ s3://mis-backups-pg/ --storage-class STANDARD_IA
Comparativa de Métodos de Backup
| Método | Velocidad | Tamaño | Restauración Selectiva | Soporte PITR | Caso de Uso |
|---|---|---|---|---|---|
pg_dump (personalizado) | Media | Pequeño | Sí | No | Backup diario por base |
pg_dump (SQL plano) | Media | Grande | No | No | Bases pequeñas, portabilidad |
pg_dumpall | Lenta | Grande | No | No | Clúster completo + roles |
pg_basebackup | Rápida | Dir. completo | No | Sí (con WAL) | PITR base, standby |
| Archivado WAL | Continuo | Variable | No | Sí | Protección continua |
Escenario Real: Recuperación ante Desastres
Situación: Su servidor PostgreSQL 16 en producción falla a las 15:47 un martes. El último pg_basebackup se ejecutó el domingo a la 01:00. El archivado WAL a S3 ha funcionado continuamente.
Objetivo: Restaurar al estado de las 15:45.
Paso 1 — Aprovisionar un Nuevo Servidor
sudo systemctl stop postgresql
sudo rm -rf /var/lib/postgresql/16/main/*
Paso 2 — Restaurar el Backup Base
cd /var/lib/postgresql/16/main
sudo -u postgres tar -xzf /backup/base/20260320/base.tar.gz .
sudo -u postgres tar -xzf /backup/base/20260320/pg_wal.tar.gz pg_wal/
Paso 3 — Configurar la Recuperación
restore_command = 'aws s3 cp s3://mis-backups-pg/wal/%f %p'
recovery_target_time = '2026-03-22 15:45:00'
recovery_target_action = 'promote'
sudo -u postgres touch /var/lib/postgresql/16/main/recovery.signal
sudo systemctl start postgresql
sudo tail -f /var/log/postgresql/postgresql-16-main.log
Paso 4 — Verificar Datos y Reanudar Operaciones
SELECT COUNT(*) FROM pedidos WHERE created_at < '2026-03-22 15:46:00';
Casos Especiales y Errores Comunes
- Bases de datos grandes (>100 GB): Use
pg_dump -jypg_restore -jpara dumps y restauraciones paralelas. - Bloqueos durante el backup:
pg_dumpadquiere un bloqueoACCESS SHARE. Ejecute backups en ventanas de bajo tráfico. - Diferencias de codificación: Cree la base destino con el mismo encoding y locale que el origen.
- Secuencias: El formato personalizado captura los valores de secuencia al momento del dump.
- Extensiones: Asegúrese de que las mismas extensiones estén disponibles en el servidor de restauración.
- Tablespaces personalizados: Deben existir con las rutas correctas antes de ejecutar
pg_restore.
Solución de Problemas
| Problema | Causa Probable | Solución |
|---|---|---|
ERROR: permission denied en pg_dump | El rol carece de privilegios de lectura | Otorgue pg_read_all_data o use el superusuario postgres |
ERROR: relation already exists en pg_restore | Restaurando en una base no vacía | Elimine y recree la base destino o use el flag --clean |
archive_command falla silenciosamente | Ruta incorrecta o permisos | Verifique pg_stat_archiver.last_failed_wal y pruebe el comando manualmente |
| PITR bloqueado reproduciendo WAL | recovery_target_time en el futuro o zona horaria incorrecta | Use marcas de tiempo UTC; verifique show timezone en psql |
pg_basebackup: error: could not connect | Sin entrada en pg_hba.conf | Agregue host replication replicator 127.0.0.1/32 md5 a pg_hba.conf |
| Restauración muy lenta | Restauración monohilo de un dump grande | Use pg_restore -j N donde N = número de núcleos CPU |
Resumen
- Use pg_dump (formato personalizado) diariamente para backups lógicos por base con restauración selectiva.
- Use pg_dumpall —globals-only diariamente para capturar roles y tablespaces.
- Use pg_basebackup semanalmente como base binaria para restauración completa rápida.
- Habilite el archivado WAL para protección continua y PITR.
- Cifre los backups con GPG y almacénelos en S3 u otro almacenamiento externo.
- Pruebe las restauraciones regularmente — un backup no probado no es un backup.
- Use cron + scripts de rotación para automatizar y eliminar backups antiguos.