Backup e restauração no PostgreSQL é a base de qualquer plano de recuperação de desastres em banco de dados. Seja protegendo um único banco de dados de aplicação ou gerenciando centenas de instâncias, dominar pg_dump e pg_restore garante que você possa se recuperar de falhas de hardware, exclusões acidentais e migrações mal executadas. Este guia cobre todos os aspectos práticos — desde dumps básicos até agendamento automatizado, restaurações seletivas e a etapa crítica que a maioria das equipes ignora: testar de verdade se os backups podem ser restaurados.

Pré-requisitos

  • PostgreSQL 12 ou superior instalado e em execução
  • Acesso a um banco de dados PostgreSQL com privilégios suficientes (pg_dump exige pelo menos acesso de leitura a todos os objetos)
  • Acesso sudo ou ao usuário postgres no servidor de banco de dados
  • Familiaridade básica com SQL e conceitos do PostgreSQL (bancos de dados, esquemas, tabelas)

Criando Backups com pg_dump

pg_dump produz backups lógicos — uma representação do banco de dados como instruções SQL ou um arquivo comprimido. Ao contrário de cópias no nível do sistema de arquivos, esses backups são portáveis entre versões e plataformas do PostgreSQL.

Dump SQL Simples (Padrão)

O backup mais simples cria um arquivo .sql contendo todos os comandos SQL necessários para recriar o banco de dados:

# Dump de um único banco de dados para um arquivo SQL
pg_dump -U postgres -h localhost mydb > mydb_backup.sql

# Incluir instrução CREATE DATABASE
pg_dump -U postgres -h localhost -C mydb > mydb_backup_with_create.sql

# Dump com timestamp no nome do arquivo
pg_dump -U postgres mydb > "mydb_$(date +%Y%m%d_%H%M%S).sql"

O formato SQL simples é legível por humanos e pode ser restaurado com psql. A desvantagem: não suporta restauração paralela nem restauração seletiva de tabelas.

Formato Customizado (-Fc) — Recomendado para Produção

O formato customizado cria um arquivo comprimido não textual que suporta as opções de restauração mais flexíveis:

# Backup em formato customizado (comprimido por padrão)
pg_dump -U postgres -Fc mydb > mydb_backup.dump

# Formato customizado com compressão máxima
pg_dump -U postgres -Fc -Z 9 mydb > mydb_backup.dump

# Formato customizado com dump paralelo (4 jobs)
pg_dump -U postgres -Fc -j 4 mydb > mydb_backup.dump

Por que o formato customizado é preferido:

  • Comprimido automaticamente (tipicamente 5-10x menor que SQL simples)
  • Suporta restauração paralela com pg_restore -j
  • Permite restauração seletiva (tabelas, esquemas ou apenas dados específicos)
  • Pode reordenar itens durante a restauração para otimizar a velocidade de carga

Formato Diretório (-Fd) — Melhor para Bancos de Dados Grandes

O formato diretório cria um diretório com um arquivo por tabela, permitindo dumps verdadeiramente paralelos:

# Formato diretório com dump paralelo (8 workers)
pg_dump -U postgres -Fd -j 8 -f /backup/mydb_dir mydb

Isso cria um diretório contendo um arquivo toc.dat (índice de conteúdo) e um arquivo comprimido por tabela. O dump paralelo reduz significativamente o tempo de backup para bancos de dados grandes.

Fazendo Dump de Todos os Bancos (pg_dumpall)

Para fazer backup de todos os bancos de dados no cluster PostgreSQL, além dos objetos globais (roles, tablespaces):

# Dump de todos os bancos de dados e objetos globais
pg_dumpall -U postgres > all_databases.sql

# Dump apenas dos objetos globais (roles, tablespaces)
pg_dumpall -U postgres --globals-only > globals.sql

pg_dumpall sempre produz formato SQL simples. Para clusters grandes, faça dump dos globals separadamente e use pg_dump por banco de dados em formato customizado.

Dumps Seletivos

# Dump de uma única tabela
pg_dump -U postgres -t users mydb > users_table.sql

# Dump de múltiplas tabelas específicas
pg_dump -U postgres -t users -t orders -t products mydb > selected_tables.sql

# Dump de um esquema específico
pg_dump -U postgres -n public mydb > public_schema.sql

# Dump apenas do esquema (sem dados)
pg_dump -U postgres -s mydb > schema_only.sql

# Dump apenas dos dados (sem esquema)
pg_dump -U postgres -a mydb > data_only.sql

# Excluir uma tabela grande do dump
pg_dump -U postgres -T audit_log mydb > mydb_no_audit.sql

Restaurando Backups

Restaurar a partir de SQL Simples

# Restaurar em um banco de dados existente
psql -U postgres -h localhost mydb < mydb_backup.sql

# Criar o banco de dados e restaurar (se o dump inclui a flag -C)
psql -U postgres -h localhost < mydb_backup_with_create.sql

# Restaurar com saída detalhada
psql -U postgres -v ON_ERROR_STOP=1 mydb < mydb_backup.sql

A flag ON_ERROR_STOP=1 faz o psql parar no primeiro erro em vez de continuar silenciosamente. Sempre use isso ao restaurar bancos de dados de produção.

Restaurar a partir do Formato Customizado

# Restauração básica
pg_restore -U postgres -d mydb mydb_backup.dump

# Restauração paralela (8 jobs — dramaticamente mais rápida para bancos grandes)
pg_restore -U postgres -d mydb -j 8 mydb_backup.dump

# Restaurar em um novo banco de dados
createdb -U postgres mydb_restored
pg_restore -U postgres -d mydb_restored mydb_backup.dump

# Limpar (apagar) objetos existentes antes da restauração
pg_restore -U postgres -d mydb --clean --if-exists mydb_backup.dump

Restaurar Tabelas Específicas

# Restaurar uma única tabela de um dump em formato customizado
pg_restore -U postgres -d mydb -t users mydb_backup.dump

# Restaurar usando um arquivo de lista (controle granular)
pg_restore -l mydb_backup.dump > restore_list.txt
# Edite restore_list.txt — comente os itens que não deseja restaurar
pg_restore -U postgres -d mydb -L restore_list.txt mydb_backup.dump

Restaurar Esquema Específico

# Restaurar apenas o esquema 'public'
pg_restore -U postgres -d mydb -n public mydb_backup.dump

# Restaurar apenas dados (esquema já existe)
pg_restore -U postgres -d mydb -a mydb_backup.dump

Comparação dos Métodos de Backup no PostgreSQL

Característicapg_dump (Lógico)pg_basebackup (Físico)Snapshot de Sistema de Arquivos
Escopo do backupBanco únicoCluster completoCluster completo
Restauração entre versõesSimNão (mesma versão principal)Não
Restauração seletiva de tabelaSim (flag -t)NãoNão
Recuperação point-in-timeNãoSim (com WAL)Sim (com WAL)
Backup com sistema em execuçãoSim (snapshot consistente)SimRequer fsync/freeze
Tamanho do backupMenor (dados comprimidos)Maior (diretório de dados completo)Maior (disco completo)
Velocidade do backupMais lento (leitura via SQL)Mais rápido (streaming)Mais rápido (nível de bloco)
Melhor paraBancos individuais, migraçõesDR completo do cluster, PITRSnapshots de VM/cloud

Use pg_dump para backups diários de bancos de dados individuais e para migrações entre versões do PostgreSQL. Use pg_basebackup quando precisar de recuperação point-in-time ou estiver configurando replicação em streaming. Use snapshots de sistema de arquivos como complemento, não substituto — são rápidos, mas exigem tratamento cuidadoso dos arquivos WAL.

Automatizando Backups com Cron

Script de Backup

#!/bin/bash
# /usr/local/bin/pg_backup.sh
# Automated PostgreSQL backup script

set -euo pipefail

# Configuration
BACKUP_DIR="/var/backups/postgresql"
RETENTION_DAYS=30
PG_USER="postgres"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/log/pg_backup.log"

# Create backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"

# Log function
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

log "Starting PostgreSQL backup"

# Dump globals (roles, tablespaces)
pg_dumpall -U "$PG_USER" --globals-only > "$BACKUP_DIR/globals_${TIMESTAMP}.sql" 2>> "$LOG_FILE"
log "Globals dumped"

# Dump each database in custom format
for DB in $(psql -U "$PG_USER" -At -c "SELECT datname FROM pg_database WHERE datistemplate = false AND datname != 'postgres'"); do
    DUMP_FILE="$BACKUP_DIR/${DB}_${TIMESTAMP}.dump"
    pg_dump -U "$PG_USER" -Fc -Z 6 "$DB" > "$DUMP_FILE" 2>> "$LOG_FILE"
    SIZE=$(du -h "$DUMP_FILE" | cut -f1)
    log "Dumped $DB ($SIZE)"
done

# Remove backups older than retention period
find "$BACKUP_DIR" -name "*.dump" -mtime +${RETENTION_DAYS} -delete
find "$BACKUP_DIR" -name "globals_*.sql" -mtime +${RETENTION_DAYS} -delete
log "Cleaned up backups older than $RETENTION_DAYS days"

log "Backup complete"
# Tornar executável
chmod +x /usr/local/bin/pg_backup.sh

# Testar manualmente primeiro
sudo -u postgres /usr/local/bin/pg_backup.sh

Agendamento com Cron

# Editar o crontab do usuário postgres
sudo crontab -u postgres -e
# Backup diário às 2:00 AM
0 2 * * * /usr/local/bin/pg_backup.sh

# Backup completo semanal no domingo à 1:00 AM (manter por mais tempo)
0 1 * * 0 /usr/local/bin/pg_backup.sh

Autenticação Sem Senha com .pgpass

Para jobs cron, é necessário autenticação sem senha. Crie um arquivo .pgpass:

# Criar .pgpass para o usuário postgres
sudo -u postgres bash -c 'cat > ~/.pgpass << EOF
localhost:5432:*:postgres:your_secure_password
EOF'

# Definir permissões necessárias (pg_dump recusa usar um .pgpass legível por todos)
sudo -u postgres chmod 600 ~/.pgpass

Formato: hostname:porta:banco:usuario:senha. Use * como coringa para o banco de dados para cobrir todos os bancos.

Testando a Integridade do Backup

Um backup que não pode ser restaurado não é um backup. Agende testes regulares de restauração:

#!/bin/bash
# /usr/local/bin/pg_restore_test.sh
# Verify backup integrity by restoring to a temporary database

set -euo pipefail

BACKUP_FILE="$1"
TEST_DB="restore_test_$(date +%s)"
PG_USER="postgres"

# Create temporary database
createdb -U "$PG_USER" "$TEST_DB"

# Attempt restore
if pg_restore -U "$PG_USER" -d "$TEST_DB" "$BACKUP_FILE" 2>/dev/null; then
    # Verify data
    TABLES=$(psql -U "$PG_USER" -At -c "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public'" "$TEST_DB")
    ROWS=$(psql -U "$PG_USER" -At -c "SELECT sum(n_live_tup) FROM pg_stat_user_tables" "$TEST_DB")
    echo "PASS: Restored $TABLES tables with $ROWS total rows"
else
    echo "FAIL: Restore encountered errors"
fi

# Clean up
dropdb -U "$PG_USER" "$TEST_DB"
# Testar o backup mais recente
sudo -u postgres /usr/local/bin/pg_restore_test.sh /var/backups/postgresql/mydb_latest.dump

Solução de Problemas Comuns

”pg_dump: error: connection to server failed"

# Verificar se o PostgreSQL está em execução
sudo systemctl status postgresql

# Verificar se você consegue conectar
psql -U postgres -h localhost -l

# Verificar pg_hba.conf para regras de autenticação
sudo cat /etc/postgresql/16/main/pg_hba.conf | grep -v '^#' | grep -v '^$'

"pg_restore: error: could not execute query: ERROR: relation already exists”

O banco de dados de destino já possui objetos. Use --clean --if-exists para apagar os objetos existentes antes da restauração:

pg_restore -U postgres -d mydb --clean --if-exists mydb_backup.dump

Ou restaure em um banco de dados novo e vazio:

dropdb -U postgres mydb
createdb -U postgres mydb
pg_restore -U postgres -d mydb mydb_backup.dump

Backup muito lento

# Usar dump paralelo (somente para formato customizado ou diretório)
pg_dump -U postgres -Fd -j $(nproc) -f /backup/mydb_dir mydb

# Excluir tabelas grandes que podem ser regeneradas
pg_dump -U postgres -Fc -T large_cache_table -T session_data mydb > mydb.dump

# Comprimir menos agressivamente para ganhar velocidade
pg_dump -U postgres -Fc -Z 1 mydb > mydb_fast.dump

Restauração muito lenta

# Usar restauração paralela (formato customizado ou diretório)
pg_restore -U postgres -d mydb -j $(nproc) mydb_backup.dump

# Desabilitar triggers durante a carga de dados (acelera significativamente)
pg_restore -U postgres -d mydb --disable-triggers mydb_backup.dump

# Aumentar maintenance_work_mem para a sessão de restauração
psql -U postgres -d mydb -c "SET maintenance_work_mem = '1GB';"
pg_restore -U postgres -d mydb mydb_backup.dump

Arquivo .pgpass ignorado

# Verificar permissões (deve ser 600)
ls -la ~/.pgpass

# Corrigir permissões
chmod 600 ~/.pgpass

# Verificar formato (sem espaços extras)
cat ~/.pgpass
# Deve ser: hostname:porta:banco:usuario:senha

Casos Especiais e Armadilhas

Objetos grandes (BLOBs): pg_dump inclui objetos grandes por padrão, mas pg_restore -t NÃO os restaura. Use pg_restore -L com uma lista customizada para incluir objetos grandes em restaurações seletivas.

Extensões: pg_dump inclui instruções CREATE EXTENSION, mas a restauração falha se a extensão não estiver instalada no servidor de destino. Instale as extensões antes de restaurar.

Propriedade e permissões: pg_dump registra a propriedade dos objetos. Se o role proprietário não existir no servidor de destino, a restauração falha. Use --no-owner para ignorar atribuições de propriedade, ou crie os roles primeiro usando o dump de globals.

Acesso concorrente durante o dump: pg_dump tira um snapshot no início e lê consistentemente a partir dele. Outras transações continuam normalmente. Porém, operações DDL (ALTER TABLE, DROP) que mantêm AccessExclusiveLock bloquearão o dump. Agende dumps em períodos de baixa atividade.

Incompatibilidade de encoding: Se os bancos de dados de origem e destino usam encodings diferentes, pode ocorrer corrupção de dados. Sempre verifique se os encodings coincidem:

psql -U postgres -c "SHOW server_encoding;"

Resumo

  • pg_dump com formato customizado (-Fc) é a abordagem recomendada para backups de produção — comprime os dados, suporta restauração paralela e permite recuperação seletiva de tabelas
  • pg_dumpall captura objetos globais (roles, tablespaces) que pg_dump não inclui — sempre faça dump dos globals separadamente junto com seus backups de banco de dados
  • Automatize com cron e um script de backup que inclua rotação, log e tratamento de erros — nunca dependa de procedimentos manuais de backup
  • Teste suas restaurações regularmente restaurando em um banco de dados temporário e validando contagens de linhas — um backup que você nunca testou é um backup que pode falhar quando você precisar
  • Use .pgpass para autenticação sem senha em jobs cron com permissões chmod 600 — pg_dump recusa ler arquivos de credenciais legíveis por todos
  • Dump e restauração paralelos (flag -j) reduzem drasticamente o tempo para bancos de dados grandes — use o formato diretório (-Fd) para o melhor desempenho paralelo

Artigos Relacionados