Copia de Seguridad Comprimida con tar y gzip: Guía Práctica Completa

Introducción

La utilidad tar (Tape Archive), combinada con la compresión gzip, ha sido una piedra angular de las copias de seguridad en sistemas Unix/Linux durante décadas. A pesar de la aparición de herramientas modernas de respaldo con características avanzadas como deduplicación y cifrado, tar y gzip siguen siendo relevantes debido a su universalidad, simplicidad y fiabilidad. Cada sistema Linux incluye estas herramientas por defecto, lo que las hace ideales para situaciones donde necesitas copias de seguridad portables y autocontenidas que pueden restaurarse en cualquier sistema tipo Unix sin instalar software especializado.

Comprender tar y gzip es fundamental para cualquier administrador de sistemas Linux. Estas herramientas proporcionan la base para muchos flujos de trabajo de respaldo, desde archivos de archivos simples hasta sistemas de respaldo automatizados complejos. Su operación directa, combinada con opciones poderosas para respaldos selectivos, compresión y archivo incremental, las hace adecuadas para todo, desde respaldos personales rápidos hasta implementaciones de recuperación ante desastres empresariales siguiendo la regla de respaldo 3-2-1.

Esta guía completa explora tar y gzip desde los conceptos básicos hasta el uso avanzado en producción, cubriendo sintaxis, estrategias de compresión, respaldos incrementales, automatización, procedimientos de restauración y escenarios del mundo real.

Comprendiendo tar y gzip

¿Qué es tar?

Tar (Tape Archive) crea archivos de archivo combinando múltiples archivos y directorios en un solo archivo. Originalmente diseñado para respaldos en cinta magnética, tar ahora sirve como un formato de archivo universal.

Características clave:

  • Preserva los metadatos de archivos (permisos, propiedad, marcas de tiempo)
  • Mantiene las estructuras de directorios
  • Puede archivar archivos especiales (enlaces simbólicos, dispositivos)
  • Crea archivos secuenciales adecuados para streaming
  • Sin compresión por defecto (tar en sí no comprime)

¿Qué es gzip?

Gzip (GNU zip) es una utilidad de compresión que reduce el tamaño de los archivos utilizando el algoritmo DEFLATE. Mientras gzip comprime archivos, tradicionalmente opera en archivos individuales a la vez.

Características clave:

  • Buena relación de compresión (típicamente reducción del 50-80%)
  • Compresión/descompresión rápida
  • Disponible en prácticamente todos los sistemas Unix/Linux
  • Se combina naturalmente con tar para archivos comprimidos

Flujo de trabajo tar + gzip

El flujo de trabajo estándar combina ambos:

# Create tar archive
tar -cf archive.tar /path/to/backup

# Compress with gzip
gzip archive.tar
# Result: archive.tar.gz

# Or combine in single command
tar -czf archive.tar.gz /path/to/backup

Operaciones Básicas de tar

Creando Archivos

Sintaxis básica:

tar -cf archive.tar file1 file2 directory/

Opciones esenciales:

  • -c: Crear archivo
  • -f: Especificar nombre de archivo
  • -v: Salida detallada
  • -z: Compresión gzip
  • -j: Compresión bzip2
  • -J: Compresión xz
  • -t: Listar contenido
  • -x: Extraer archivo

Crear archivo básico:

# Archive single directory
tar -cf backup.tar /home/user/

# Archive multiple items
tar -cf backup.tar /etc/ /var/www/ /home/

# Verbose output
tar -cvf backup.tar /home/user/

# Archive with gzip compression
tar -czf backup.tar.gz /home/user/

# Archive with bzip2 (better compression, slower)
tar -cjf backup.tar.bz2 /home/user/

# Archive with xz (best compression, slowest)
tar -cJf backup.tar.xz /home/user/

Eliminar barra diagonal inicial (recomendado para portabilidad):

# Keep leading slash (absolute paths)
tar -czf backup.tar.gz /home/user/
# Extracts to: /home/user/

# Remove leading slash (relative paths)
tar -czf backup.tar.gz -C / home/user/
# or
tar -czf backup.tar.gz --exclude-vcs /home/user/

Listando Contenido de Archivos

# List files in archive
tar -tf archive.tar.gz

# Verbose listing with details
tar -tvf archive.tar.gz

# List specific files matching pattern
tar -tf archive.tar.gz | grep '.conf$'

# Count files in archive
tar -tf archive.tar.gz | wc -l

Extrayendo Archivos

# Extract to current directory
tar -xzf archive.tar.gz

# Extract to specific directory
tar -xzf archive.tar.gz -C /restore/location/

# Extract specific files
tar -xzf archive.tar.gz path/to/specific/file.txt

# Extract with verbose output
tar -xzvf archive.tar.gz

# Extract files matching pattern
tar -xzf archive.tar.gz --wildcards '*.conf'

# Extract and preserve permissions (default)
tar -xzpf archive.tar.gz

# Overwrite existing files
tar -xzf archive.tar.gz --overwrite

Opciones y Estrategias de Compresión

Comparando Algoritmos de Compresión

gzip (por defecto, equilibrado):

tar -czf archive.tar.gz /data/
# Compression ratio: ~60-70%
# Speed: Fast
# Compatibility: Universal

bzip2 (mejor compresión):

tar -cjf archive.tar.bz2 /data/
# Compression ratio: ~70-80%
# Speed: Slower than gzip
# Compatibility: Very good

xz (mejor compresión):

tar -cJf archive.tar.xz /data/
# Compression ratio: ~80-90%
# Speed: Slowest
# Compatibility: Good (modern systems)

lz4 (más rápido):

tar -c /data/ | lz4 > archive.tar.lz4
# Compression ratio: ~50-60%
# Speed: Very fast
# Compatibility: Requires lz4 installed

Ejemplo de comparación de rendimiento:

#!/bin/bash
# Compare compression methods

SOURCE="/var/www"
RESULTS="compression-test-results.txt"

echo "Compression Performance Test" > "$RESULTS"
echo "Source: $SOURCE" >> "$RESULTS"
echo "Original size: $(du -sh $SOURCE | cut -f1)" >> "$RESULTS"
echo "" >> "$RESULTS"

# gzip
echo "Testing gzip..." >> "$RESULTS"
time tar -czf test-gzip.tar.gz "$SOURCE" 2>&1 | grep real >> "$RESULTS"
echo "Size: $(du -h test-gzip.tar.gz | cut -f1)" >> "$RESULTS"
echo "" >> "$RESULTS"

# bzip2
echo "Testing bzip2..." >> "$RESULTS"
time tar -cjf test-bzip2.tar.bz2 "$SOURCE" 2>&1 | grep real >> "$RESULTS"
echo "Size: $(du -h test-bzip2.tar.bz2 | cut -f1)" >> "$RESULTS"
echo "" >> "$RESULTS"

# xz
echo "Testing xz..." >> "$RESULTS"
time tar -cJf test-xz.tar.xz "$SOURCE" 2>&1 | grep real >> "$RESULTS"
echo "Size: $(du -h test-xz.tar.xz | cut -f1)" >> "$RESULTS"

cat "$RESULTS"

Ajuste de Nivel de Compresión

Niveles de compresión gzip (1-9):

# Fast compression (level 1)
tar -czf archive.tar.gz --gzip-level=1 /data/

# Default (level 6)
tar -czf archive.tar.gz /data/

# Maximum compression (level 9)
tar -czf archive.tar.gz --gzip-level=9 /data/

Usando pigz (gzip paralelo):

# Install pigz
sudo apt install pigz  # Ubuntu/Debian
sudo yum install pigz  # CentOS/RHEL

# Use with tar
tar -cf - /data/ | pigz -p 4 > archive.tar.gz
# -p 4: Use 4 CPU cores

# Extract
pigz -dc archive.tar.gz | tar -xf -

Estrategia de Compresión Óptima

Elegir según el caso de uso:

Respaldos rápidos (priorizar velocidad):

tar -czf --gzip-level=1 backup.tar.gz /data/
# or
tar -c /data/ | lz4 > backup.tar.lz4

Espacio limitado (priorizar tamaño):

tar -cJf backup.tar.xz /data/
# or
tar -cjf backup.tar.bz2 /data/

Equilibrado (por defecto en producción):

tar -czf backup.tar.gz /data/
# Standard gzip, good balance

Conjuntos de datos grandes con CPU multi-núcleo:

tar -cf - /data/ | pigz -p $(nproc) > backup.tar.gz
# Parallel compression using all cores

Características Avanzadas de tar

Respaldos Incrementales

Tar admite respaldos incrementales utilizando archivos de instantáneas:

Crear archivo de instantánea:

# Full backup with snapshot
tar -czf full-backup.tar.gz \
    --listed-incremental=backup.snar \
    /home/user/

Respaldo incremental (solo archivos modificados):

# Incremental backup 1
tar -czf incremental-1.tar.gz \
    --listed-incremental=backup.snar \
    /home/user/

# Incremental backup 2
tar -czf incremental-2.tar.gz \
    --listed-incremental=backup.snar \
    /home/user/

Restaurar respaldos incrementales:

# Restore must be in order
tar -xzf full-backup.tar.gz --listed-incremental=/dev/null
tar -xzf incremental-1.tar.gz --listed-incremental=/dev/null
tar -xzf incremental-2.tar.gz --listed-incremental=/dev/null

Excluyendo Archivos y Directorios

Patrones de exclusión:

# Exclude specific files/directories
tar -czf backup.tar.gz \
    --exclude='*.log' \
    --exclude='*.tmp' \
    --exclude='cache' \
    --exclude='node_modules' \
    /var/www/

Archivo de exclusión:

Crear /etc/tar-exclude.txt:

*.log
*.tmp
.cache
cache/
tmp/
*.swp
node_modules/
vendor/
__pycache__/
.git/

Usar con tar:

tar -czf backup.tar.gz \
    --exclude-from=/etc/tar-exclude.txt \
    /var/www/

Excluir control de versiones:

tar -czf backup.tar.gz --exclude-vcs /project/
# Excludes .git, .svn, .hg, etc.

Inclusión Selectiva de Archivos

Incluir solo archivos específicos:

# Backup only .conf files
tar -czf configs.tar.gz \
    --wildcards \
    --no-recursion \
    $(find /etc -name '*.conf')

Respaldar tipos de archivos específicos:

# All PHP and HTML files
find /var/www -name '*.php' -o -name '*.html' | \
    tar -czf web-files.tar.gz -T -

Preservando Metadatos y Permisos

# Preserve all attributes (default)
tar -czpf backup.tar.gz /data/

# Preserve permissions (-p)
tar -czpf backup.tar.gz /data/

# Preserve SELinux context
tar -czf backup.tar.gz --selinux /data/

# Preserve extended attributes
tar -czf backup.tar.gz --xattrs /data/

# Preserve ACLs
tar -czf backup.tar.gz --acls /data/

# All preservation options
tar -czf backup.tar.gz \
    --preserve-permissions \
    --selinux \
    --xattrs \
    --acls \
    /data/

Scripts de Respaldo en Producción

Script de Respaldo Completo

#!/bin/bash
# /usr/local/bin/tar-backup.sh
# Production tar-based backup with rotation and verification

set -euo pipefail

# Configuration
BACKUP_NAME="backup-$(hostname)-$(date +%Y%m%d-%H%M%S)"
BACKUP_ROOT="/backup"
BACKUP_PATH="$BACKUP_ROOT/$BACKUP_NAME.tar.gz"
LOG_FILE="/var/log/tar-backup.log"
RETENTION_DAYS=30
ADMIN_EMAIL="[email protected]"

# Sources to backup
BACKUP_SOURCES=(
    "/etc"
    "/home"
    "/var/www"
    "/opt"
    "/root"
)

# Exclude file
EXCLUDE_FILE="/etc/tar-exclude.txt"

# Logging
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

error_exit() {
    log "ERROR: $1"
    echo "Backup failed: $1" | mail -s "Backup FAILED - $(hostname)" "$ADMIN_EMAIL"
    exit 1
}

# Create exclude file if doesn't exist
if [ ! -f "$EXCLUDE_FILE" ]; then
    cat > "$EXCLUDE_FILE" << 'EOF'
*.log
*.tmp
.cache/
tmp/
cache/
node_modules/
vendor/
__pycache__/
.git/
lost+found/
*.swp
EOF
fi

log "Starting backup: $BACKUP_NAME"

# Pre-backup: Database dumps
log "Creating database dumps"
mkdir -p /var/backups/db-dumps

if command -v mysqldump &> /dev/null; then
    mysqldump --all-databases --single-transaction | \
        gzip > /var/backups/db-dumps/mysql-all.sql.gz
fi

if command -v pg_dumpall &> /dev/null; then
    sudo -u postgres pg_dumpall | \
        gzip > /var/backups/db-dumps/postgresql-all.sql.gz
fi

# Create tar backup with parallel compression
log "Creating compressed archive"
tar -cf - \
    --exclude-from="$EXCLUDE_FILE" \
    --exclude-caches \
    --exclude-vcs \
    "${BACKUP_SOURCES[@]}" \
    /var/backups/db-dumps \
    2>> "$LOG_FILE" | \
    pigz -p $(nproc) > "$BACKUP_PATH"

if [ ${PIPESTATUS[0]} -ne 0 ]; then
    error_exit "Tar command failed"
fi

# Verify archive integrity
log "Verifying archive integrity"
if ! gzip -t "$BACKUP_PATH" 2>> "$LOG_FILE"; then
    error_exit "Archive integrity check failed"
fi

# Check archive size
ARCHIVE_SIZE=$(stat -c%s "$BACKUP_PATH" 2>/dev/null || stat -f%z "$BACKUP_PATH")
MIN_SIZE=1048576  # 1MB minimum

if [ "$ARCHIVE_SIZE" -lt "$MIN_SIZE" ]; then
    error_exit "Archive suspiciously small: $ARCHIVE_SIZE bytes"
fi

# Create checksum
log "Creating checksum"
sha256sum "$BACKUP_PATH" > "$BACKUP_PATH.sha256"

# Create manifest
log "Creating manifest"
cat > "$BACKUP_ROOT/$BACKUP_NAME.txt" << EOF
Backup Manifest
Date: $(date)
Server: $(hostname)
Archive: $BACKUP_NAME.tar.gz
Size: $(du -h "$BACKUP_PATH" | cut -f1)
Checksum: $(cat "$BACKUP_PATH.sha256")

Sources:
$(printf '%s\n' "${BACKUP_SOURCES[@]}")

File count: $(tar -tzf "$BACKUP_PATH" | wc -l)
EOF

# Cleanup old backups
log "Cleaning up old backups"
find "$BACKUP_ROOT" -name "backup-*.tar.gz" -mtime +$RETENTION_DAYS -delete
find "$BACKUP_ROOT" -name "backup-*.txt" -mtime +$RETENTION_DAYS -delete
find "$BACKUP_ROOT" -name "backup-*.sha256" -mtime +$RETENTION_DAYS -delete

# Cleanup old database dumps
find /var/backups/db-dumps -name "*.sql.gz" -mtime +3 -delete

log "Backup completed successfully"

# Success notification
{
    echo "Backup completed successfully"
    echo ""
    cat "$BACKUP_ROOT/$BACKUP_NAME.txt"
} | mail -s "Backup Success - $(hostname)" "$ADMIN_EMAIL"

exit 0

Hacer ejecutable:

sudo chmod +x /usr/local/bin/tar-backup.sh

Script de Respaldo Rotativo con Retención Escalonada

#!/bin/bash
# /usr/local/bin/tar-rotating-backup.sh
# Implements GFS (Grandfather-Father-Son) rotation

BACKUP_TYPE="$1"  # daily, weekly, or monthly
BACKUP_ROOT="/backup/tar"
DATE=$(date +%Y%m%d)
SOURCES="/etc /home /var/www"

case "$BACKUP_TYPE" in
    daily)
        BACKUP_FILE="$BACKUP_ROOT/daily/backup-daily-$DATE.tar.gz"
        KEEP_DAYS=7
        ;;
    weekly)
        BACKUP_FILE="$BACKUP_ROOT/weekly/backup-weekly-$(date +%YW%V).tar.gz"
        KEEP_DAYS=28
        ;;
    monthly)
        BACKUP_FILE="$BACKUP_ROOT/monthly/backup-monthly-$(date +%Y%m).tar.gz"
        KEEP_DAYS=365
        ;;
    *)
        echo "Usage: $0 {daily|weekly|monthly}"
        exit 1
        ;;
esac

# Create backup directory
mkdir -p "$(dirname "$BACKUP_FILE")"

# Create backup
echo "Creating $BACKUP_TYPE backup: $BACKUP_FILE"
tar -czf "$BACKUP_FILE" \
    --exclude='*.log' \
    --exclude='cache' \
    --exclude='tmp' \
    $SOURCES

# Cleanup old backups
find "$(dirname "$BACKUP_FILE")" -name "*.tar.gz" -mtime +$KEEP_DAYS -delete

echo "$BACKUP_TYPE backup completed: $BACKUP_FILE"

Programación Cron:

# Daily backup at 2 AM
0 2 * * * /usr/local/bin/tar-rotating-backup.sh daily

# Weekly backup on Sunday at 3 AM
0 3 * * 0 /usr/local/bin/tar-rotating-backup.sh weekly

# Monthly backup on 1st at 4 AM
0 4 1 * * /usr/local/bin/tar-rotating-backup.sh monthly

Procedimientos de Restauración

Restauración Básica

# Extract entire archive
tar -xzf backup.tar.gz

# Extract to specific location
tar -xzf backup.tar.gz -C /restore/path/

# Extract with verbose output
tar -xzvf backup.tar.gz

# Extract and overwrite existing files
tar -xzf backup.tar.gz --overwrite

Restauración Selectiva

# List archive contents first
tar -tzf backup.tar.gz | grep important-file.txt

# Extract specific file
tar -xzf backup.tar.gz path/to/important-file.txt

# Extract directory
tar -xzf backup.tar.gz path/to/directory/

# Extract multiple items
tar -xzf backup.tar.gz \
    path/to/file1.txt \
    path/to/file2.txt \
    path/to/directory/

# Extract files matching pattern
tar -xzf backup.tar.gz --wildcards '*.conf'

# Extract to stdout (view without extracting)
tar -xzf backup.tar.gz -O path/to/file.txt

Recuperación ante Desastres

Restauración completa del sistema:

# Boot from live USB
# Mount target filesystem
mount /dev/sda1 /mnt/target

# Extract backup
cd /mnt/target
tar -xzf /backup/full-system-backup.tar.gz

# Restore bootloader
grub-install --root-directory=/mnt/target /dev/sda
update-grub

# Reboot
reboot

Verificación Después de la Restauración

# Compare restored files with archive
tar -tzf backup.tar.gz > archive-files.txt
find /restored/path -type f > restored-files.txt
diff archive-files.txt restored-files.txt

# Verify file counts match
ARCHIVE_COUNT=$(tar -tzf backup.tar.gz | wc -l)
RESTORED_COUNT=$(find /restored/path | wc -l)
echo "Archive: $ARCHIVE_COUNT | Restored: $RESTORED_COUNT"

Automatización y Monitoreo

Implementación de Temporizador Systemd

Archivo de servicio (/etc/systemd/system/tar-backup.service):

[Unit]
Description=Tar Backup Service
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/tar-backup.sh
User=root
Nice=19
IOSchedulingClass=2
IOSchedulingPriority=7

[Install]
WantedBy=multi-user.target

Archivo de temporizador (/etc/systemd/system/tar-backup.timer):

[Unit]
Description=Daily Tar Backup
Requires=tar-backup.service

[Timer]
OnCalendar=daily
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target

Habilitar:

sudo systemctl daemon-reload
sudo systemctl enable --now tar-backup.timer

Script de Monitoreo

#!/bin/bash
# /usr/local/bin/monitor-tar-backups.sh

BACKUP_DIR="/backup"
MAX_AGE_HOURS=26
ADMIN_EMAIL="[email protected]"

# Find latest backup
LATEST_BACKUP=$(find "$BACKUP_DIR" -name "backup-*.tar.gz" -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-)

if [ -z "$LATEST_BACKUP" ]; then
    echo "ERROR: No backups found" | \
        mail -s "Backup Monitoring Alert" "$ADMIN_EMAIL"
    exit 1
fi

# Check age
BACKUP_TIME=$(stat -c %Y "$LATEST_BACKUP")
CURRENT_TIME=$(date +%s)
AGE_HOURS=$(( (CURRENT_TIME - BACKUP_TIME) / 3600 ))

if [ $AGE_HOURS -gt $MAX_AGE_HOURS ]; then
    echo "WARNING: Latest backup is $AGE_HOURS hours old" | \
        mail -s "Backup Age Alert" "$ADMIN_EMAIL"
    exit 1
else
    echo "OK: Latest backup is $AGE_HOURS hours old"
    exit 0
fi

Escenarios del Mundo Real

Escenario 1: Respaldo de Sitio Web

#!/bin/bash
# Website backup with database

BACKUP_DIR="/backup/website"
DATE=$(date +%Y%m%d)

# Dump database
mysqldump website_db | gzip > /tmp/website-db.sql.gz

# Create tar backup
tar -czf "$BACKUP_DIR/website-$DATE.tar.gz" \
    --exclude='cache/*' \
    --exclude='logs/*' \
    /var/www/website/ \
    /tmp/website-db.sql.gz

# Cleanup
rm /tmp/website-db.sql.gz

# Keep 30 days
find "$BACKUP_DIR" -name "website-*.tar.gz" -mtime +30 -delete

Escenario 2: Respaldo de Configuración

#!/bin/bash
# System configuration backup

tar -czf /backup/config-$(date +%Y%m%d).tar.gz \
    /etc \
    /root/.ssh \
    /home/*/.ssh \
    /var/spool/cron \
    --exclude='/etc/shadow-' \
    --exclude='/etc/gshadow-'

Escenario 3: Respaldo Fuera del Sitio

#!/bin/bash
# Create backup and sync to remote server

# Create backup
/usr/local/bin/tar-backup.sh

# Sync to remote
rsync -avz --delete \
    /backup/ \
    user@backup-server:/backups/$(hostname)/

# Upload to S3
LATEST=$(ls -t /backup/backup-*.tar.gz | head -1)
aws s3 cp "$LATEST" s3://my-backups/$(hostname)/

Conclusión

Tar y gzip proporcionan soluciones de respaldo confiables y universales que funcionan en todos los sistemas Unix/Linux. Aunque las herramientas modernas de respaldo ofrecen características avanzadas, tar+gzip sigue siendo relevante por su simplicidad, portabilidad y efectividad.

Puntos clave:

  1. Elige la compresión sabiamente: Equilibra velocidad vs. tamaño según tus necesidades
  2. Excluye datos innecesarios: Usa patrones de exclusión para optimizar respaldos
  3. Verifica archivos: Siempre prueba la integridad del archivo después de la creación
  4. Automatiza consistentemente: Programa respaldos automáticos regulares
  5. Prueba la restauración: Practica procedimientos de restauración regularmente
  6. Implementa retención: La limpieza automática previene el agotamiento del disco
  7. Sigue 3-2-1: Respaldos tar locales, remotos y fuera del sitio

Combinado con automatización adecuada, monitoreo y la regla de respaldo 3-2-1, tar y gzip proporcionan una base sólida para la protección de datos en entornos Linux.