Guía de backup y recuperación PostgreSQL con wal‑shipping y basebackup en VPS con espacio limitado: práctica de bajo uso de disco

Por · Publicado el · 10 min de lectura

cubos y bancos bajos alineados cerca de ventanas, iluminados por luz natural
Imagen limpia sin textos, sugiere almacenamiento y espacio limitado Foto: Jimmy Liao / Pexels

Guía de backup y recuperación PostgreSQL en VPS con poco espacio. Verás configuraciones objetivas, scripts y decisiones prácticas para ahorrar disco local. El foco es mantener basebackups compactos, enviar WAL comprimido a almacenamiento externo y asegurar una recuperación punto‑en‑tiempo (PITR) comprobable, sin promesas irreales.

Escenario: VPS con poco disco y lo que realmente importa

En VPS pequeñas, el cuello de botella suele ser el disco. Los backups completos locales ocupan demasiado y complican la restauración.

La solución es reducir la huella local. Envía el historial de WAL fuera del disco principal y mantén los basebackups compactos.

Objetivo: un basebackup reciente y un flujo continuo de WAL archivado. Ambos fuera del disco principal.

Señales de que hace falta: fallos por falta de espacio, pg_wal creciendo y restauraciones lentas por copias grandes.

En los apartados siguientes abordamos decisiones prácticas: compresión, destino, retención y automatización con seguridad.

  • Evitar retención larga en el disco local
  • Enviar WAL comprimido a almacenamiento externo
  • Ejecutar basebackup con WAL separado (-X none)
  • Probar restauración y PITR con regularidad

Arquitectura recomendada: basebackup + WAL shipping comprimido

Usa dos flujos: 1) basebackup periódico y 2) WAL shipping continuo y comprimido. Ambos van a almacenamiento externo.

En el disco local mantén solo lo esencial. El historial de WAL no necesita retención local más allá del mínimo.

Políticas de ciclo de vida en el bucket controlan costes y retención de forma segura.

Si ya usas S3‑compatible, integra con snapshots y subidas incrementales en snapshots LVM + Restic en S3. En escenarios que requieren PITR granular, complementa con este flujo de WAL.

Este diseño cumple el objetivo de backup y recuperación PostgreSQL con wal‑shipping y basebackup en VPS con espacio limitado.

  • Basebackup periódico: punto inicial de la restauración
  • WAL shipping continuo: cubre el intervalo entre basebackups
  • Retención definida por políticas en el bucket
  • Alertas automáticas en caso de fallo de archivado

Configurar PostgreSQL para archiving eficiente (sin llenar el disco)

Activa el archivado y define un comando que comprima y transfiera los WAL al exterior. En versiones 13+, prefiere wal_keep_size bajo.

En versiones antiguas, ajusta wal_keep_segments. Evita retenciones locales agresivas si la meta es ahorrar disco.

Usa un wrapper script en archive_command. Gestiona compresión, upload y logs, y simplifica el mantenimiento.

# postgresql.conf (excertos)
wal_level = replica
archive_mode = on
archive_command = '/usr/local/bin/pg-archive-wal %p %f'
max_wal_size = 1GB                # ajuste conforme sua carga
# Em PG >= 13
wal_keep_size = 64MB              # manter o mínimo necessário
# Em PG <= 12 use wal_keep_segments apropriadamente baixo
# Opcional, se disponível na sua versão:
# wal_compression = on            # reduz tamanho do WAL gerado

Wrapper de archive_command: compresión + upload (S3 o SSH)

El archive_command debe ser idempotente y rápido. Si falla, PostgreSQL reintentará.

Nunca elimines archivos locales antes de confirmar el upload. Más abajo hay un wrapper con zstd y dos destinos: S3‑compatible y servidor remoto vía SSH.

Adapta variables y rutas. Registra logs. Monitorea siempre el código de retorno.

#!/usr/bin/env bash
# /usr/local/bin/pg-archive-wal
# Uso: pg-archive-wal <path_absoluto_wal> <nome_arquivo_wal>
set -euo pipefail
PATH=/usr/local/bin:/usr/bin:/bin
SRC_PATH="$1"
WAL_NAME="$2"
LOG=/var/log/postgresql/wal-archive.log
COMP_LEVEL=${COMP_LEVEL:-3}          # trade-off CPU x compactação
DEST_MODE=${DEST_MODE:-s3}           # s3|ssh

case "$DEST_MODE" in
  s3)
    : "${S3_BUCKET:?Defina S3_BUCKET}"
    # Envia fluxo comprimido (stdin) para bucket S3-compatível
    zstd -T0 -${COMP_LEVEL} -c -- "$SRC_PATH" \
      | aws s3 cp - "s3://${S3_BUCKET}/wal/${WAL_NAME}.zst" --only-show-errors \
      && echo "$(date -Is) OK S3 ${WAL_NAME}" >> "$LOG"
    ;;
  ssh)
    : "${SSH_HOST:?Defina SSH_HOST}" "${SSH_PATH:?Defina SSH_PATH}"
    zstd -T0 -${COMP_LEVEL} -c -- "$SRC_PATH" \
      | ssh -o StrictHostKeyChecking=yes "$SSH_HOST" \
        "cat > '${SSH_PATH}/wal/${WAL_NAME}.zst'" \
      && echo "$(date -Is) OK SSH ${WAL_NAME}" >> "$LOG"
    ;;
  *)
    echo "Modo de destino inválido: $DEST_MODE" >&2; exit 2;;
esac

# Importante: NÃO remover o arquivo de origem; o PostgreSQL gerencia.

Basebackup compacto (-X none) y envío directo a almacenamiento externo

Ejecuta un CHECKPOINT antes del basebackup y evita incluir WAL en el paquete (-X none). No duplicas datos.

Comprime en stream y envía al destino externo. Prueba tu versión: algunos flags cambian entre releases.

Si prefieres generar localmente, comprime después de terminar y sube. Esto reduce la ventana de inconsistencia.

# 1) Checkpoint para estabilizar o backup
psql -U postgres -d postgres -c "CHECKPOINT;"

# 2) Basebackup local, formato tar, sin WAL incluido; compacta depois
pg_basebackup -U postgres -Ft -X none -D /backups/base/$(date +%F) -v --progress
find /backups/base -type f -name "*.tar" -print0 | xargs -0 -I{} zstd -T0 -3 {}
aws s3 cp /backups/base/ s3://$S3_BUCKET/base/ --recursive --exclude "*" --include "*.zst"

# 3) Alternativa: enviar em stream direto para o bucket
pg_basebackup -U postgres -Ft -X none -D - -v --progress \
  | zstd -T0 -3 \
  | aws s3 cp - s3://$S3_BUCKET/base/$(date +%F).tar.zst --only-show-errors

# 4) Opcional: limitar taxa de leitura para proteger o I/O do servidor.

Cómo restaurar y hacer PITR usando WAL archivado (paso a paso)

El proceso: recuperar el basebackup, preparar el directorio de datos y configurar restore_command para obtener los WAL archivados.

Para recuperar hasta un instante (PITR), define un recovery_target. Atención: estos pasos eliminan datos del directorio actual. Prueba antes.

Para un caso práctico de pérdida accidental de tabla, consulta recuperar vía PITR sin downtime.

# 1) Parar o PostgreSQL e preparar diretório
sudo systemctl stop postgresql
sudo rm -rf "$PGDATA"/*   # ATENÇÃO: isso apaga dados atuais

# 2) Restaurar o basebackup
aws s3 cp s3://$S3_BUCKET/base/2023-12-01.tar.zst - \
  | zstd -d \
  | tar -x -C "$PGDATA"

# 3) Configurar restore_command e (opcional) PITR
cat >> "$PGDATA/postgresql.auto.conf" <<'EOF'
restore_command = 'aws s3 cp s3://BUCKET/wal/%f.zst - | zstd -d -c > %p'
recovery_target_time = '2023-12-01 14:32:00+00'   # comente para recuperar até o fim do WAL
EOF

# 4) Criar recovery.signal (PG >= 12)
touch "$PGDATA/recovery.signal"
chown -R postgres:postgres "$PGDATA"
chmod 700 "$PGDATA"

# 5) Subir e acompanhar logs
sudo systemctl start postgresql
# Acompanhe logs até o término do replay e remoção de recovery.signal

Retención, compresión y costes: elecciones y trade‑offs prácticos

Con disco limitado, prioriza la compresión en archive_command y una retención corta en el bucket. La compresión consume CPU; usa niveles moderados, como zstd -3.

Define políticas en el bucket para expirar WALs antiguos tras un nuevo basebackup válido. Las clases de almacenamiento más baratas reducen coste, pero aumentan la latencia de restauración.

Para un pipeline incremental y cifrado de archivos variados, evalúa los backups incrementales cifrados.

  • Rutina sugerida: basebackup semanal + WAL continuo
  • WAL: cubrir el intervalo entre basebackups, nada más
  • Política: expirar WALs tras confirmar nuevo basebackup
  • Compresión: niveles moderados para no competir con la CPU
Opción Impacto en disco Impacto en CPU Riesgo Cuándo usar
Sin compresión Mayor uso Bajo Costo y restauración más lenta CPU muy limitada
gzip nivel medio Medio Medio Subida más lenta si la CPU se satura Entorno equilibrado
zstd nivel 3 Bajo Bajo/Medio CPU adicional en picos Buena relación tasa/velocidad
Almacenamiento en clase fría Costo menor N/A Mayor latencia para recuperar Backups raramente consultados

Monitorizar el archiving, validar restauraciones y checklist final

Sin monitorización, el archivado puede fallar durante días. Observa pg_stat_archiver, el tamaño de pg_wal y las fechas en el bucket.

Programa restauraciones de prueba en un entorno aislado y validaciones de integridad. Combínalo con un checklist de hardening en checklist de seguridad para VPS Linux.

Usa esta lista para cerrar la configuración con foco en espacio y recuperación confiable.

  • archive_mode=on y archive_command probado ante fallos de red
  • Compresión habilitada con nivel moderado y validada en picos
  • Basebackup periódico con -X none y prueba de recuperación real
  • Políticas de expiración en el bucket coherentes con tu ventana de PITR
  • Alertas por retrasos en archiving y crecimiento de pg_wal
  • Procedimiento de restauración documentado, versionado y revisado
-- Verificar actividad do archiver
SELECT now() AT TIME ZONE 'utc' AS now_utc,
       last_archived_wal,
       last_archived_time AT TIME ZONE 'utc' AS last_archived_utc,
       failed_archives, stats_reset
FROM pg_stat_archiver;

-- Estimar volume de WAL local (via shell)
-- du -sh $PGDATA/pg_wal
-- Listar objetos no bucket por data

Cuándo este enfoque no es suficiente y alternativas posibles

Si la tasa de escritura es muy alta, el coste de upload y retención puede crecer rápido. Reduce el intervalo de basebackup o usa compresión más agresiva fuera de picos.

Si el bucket no es opción, usa un servidor de backup dedicado vía SSH. Combínalo con verificación periódica y rotación automatizada.

Integra con snapshots y subidas incrementales cuando tenga sentido, como en la guía de snapshots LVM + Restic en S3.

Preguntas frecuentes

¿Cuál es la frecuencia ideal del basebackup en una VPS con poco disco?

Depende del volumen de escritura y del historial deseado. En muchos casos, semanal con WAL shipping continuo equilibra espacio y tiempo de recuperación. Ajusta según el coste del bucket y los objetivos de RPO/RTO.

¿WAL shipping consume mucha CPU por la compresión?

Con compresión moderada, como zstd nivel 3, el impacto suele ser aceptable. En picos, reduce el nivel o serializa los uploads. Monitorea la saturación de CPU y ajusta al perfil de la aplicación.

¿Es seguro usar -X none en pg_basebackup?

Sí, siempre que el archivado funcione y retengas WAL suficiente para cubrir desde el momento del basebackup hasta el objetivo de recuperación. Sin WAL archivado, la restauración queda limitada al estado del basebackup.

¿Cómo lidiar con fallos en el envío al bucket?

archive_command devuelve error y PostgreSQL reintenta. Registra logs, monitoriza pg_stat_archiver y configura alertas. En fallos prolongados, pg_wal crece. Asegura margen de disco y un procedimiento de recuperación.

¿Puedo cifrar los archivos antes de enviarlos?

Sí. Puedes encapsular el flujo con cifrado antes del upload o usar cifrado en el bucket. Valora el impacto en CPU y la gestión de claves. En S3‑compatible, habilita políticas de cifrado por defecto.

¿Quieres validar tu estrategia de backup y recuperación?

Si gestionas PostgreSQL en VPS con poco disco y necesitas PITR fiable, una revisión de scripts y pruebas de restauración es el siguiente paso natural.

Hablar sobre tu escenario

Ver también