No tener backups de tu base de datos no es un riesgo técnico: es una certeza de pérdida eventual. Un disco que falla, una consulta UPDATE sin WHERE, un servidor comprometido o un deploy que salió mal pueden borrar años de datos en segundos. Los backups son la única protección real contra esos escenarios.
En esta guía aprenderás cómo hacer copias de seguridad y restaurarlas en MySQL y PostgreSQL, desde los comandos más básicos hasta estrategias para automatizar el proceso en producción.
Tipos de backup: qué estrategia elegir
Antes de ver los comandos, conviene entender qué tipos de backup existen y cuándo conviene cada uno.
Backup lógico: exporta los datos como instrucciones SQL (CREATE TABLE, INSERT...). Es lo que generan mysqldump y pg_dump. Es portable (funciona en cualquier versión compatible), fácil de inspeccionar y restaurar selectivamente, pero más lento para bases de datos muy grandes y no captura el estado exacto del disco.
Backup físico: copia los archivos del sistema de ficheros de la base de datos tal como están en disco. Es más rápido para bases de datos grandes y permite point-in-time recovery, pero requiere la misma versión del motor para restaurar. Herramientas como xtrabackup (MySQL) o pg_basebackup (PostgreSQL) hacen este tipo de backup.
Para la mayoría de proyectos pequeños y medianos, el backup lógico con mysqldump o pg_dump es suficiente y mucho más sencillo de gestionar.
MySQL: backup con mysqldump
Backup básico de una base de datos
# Exportar una base de datos completa a un archivo SQL
mysqldump -u usuario -p nombre_base_datos > backup.sql
# El -p hace que pida la contraseña de forma interactiva (más seguro que escribirla en el comando)
# Si el usuario es root:
mysqldump -u root -p mi_app_db > backup_mi_app_$(date +%Y%m%d).sql
# Verificar que el archivo se generó y tiene contenido
ls -lh backup_mi_app_$(date +%Y%m%d).sql
head -50 backup_mi_app_$(date +%Y%m%d).sql # ver las primeras líneas
Opciones importantes de mysqldump
# Backup con datos y estructura (por defecto incluye ambos)
mysqldump -u root -p mi_app_db > backup_completo.sql
# Solo la estructura, sin datos (útil para replicar el esquema)
mysqldump -u root -p --no-data mi_app_db > estructura.sql
# Solo los datos, sin la estructura
mysqldump -u root -p --no-create-info mi_app_db > datos.sql
# Backup de tablas específicas (no toda la base de datos)
mysqldump -u root -p mi_app_db clientes pedidos productos > tablas_principales.sql
# Backup de todas las bases de datos del servidor
mysqldump -u root -p --all-databases > todas_las_bases.sql
# Backup consistente para bases de datos con transacciones (InnoDB)
# --single-transaction garantiza que el backup sea consistente sin bloquear las tablas
mysqldump -u root -p --single-transaction mi_app_db > backup_consistente.sql
# Con compresión: reduce el tamaño del archivo significativamente
mysqldump -u root -p --single-transaction mi_app_db | gzip > backup_$(date +%Y%m%d).sql.gz
# Backup de un servidor remoto
mysqldump -h servidor.ejemplo.com -u usuario -p --single-transaction mi_app_db > backup.sql
Restaurar un backup en MySQL
# Crear la base de datos si no existe
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS mi_app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# Restaurar el backup
mysql -u root -p mi_app_db < backup.sql
# Restaurar un backup comprimido directamente
gunzip < backup_20260615.sql.gz | mysql -u root -p mi_app_db
# Restaurar en un servidor remoto
mysql -h servidor.ejemplo.com -u usuario -p mi_app_db < backup.sql
# Ver el progreso durante la restauración (útil para archivos grandes)
pv backup.sql | mysql -u root -p mi_app_db
# pv muestra una barra de progreso (instalar con: apt install pv)
Restaurar una sola tabla de un backup completo
# Si necesitas restaurar solo la tabla 'clientes' de un backup que tiene toda la BD:
# Opción 1: extraer la tabla del backup con grep (para tablas pequeñas)
grep -n "Table structure for table \`clientes\`" backup.sql # encontrar el número de línea
# Opción 2: usar mysqldump con --tables al hacer el backup en primer lugar
mysqldump -u root -p mi_app_db clientes > backup_clientes.sql
mysql -u root -p mi_app_db < backup_clientes.sql
# Opción 3: herramienta mysqlfrm o extraer con un script de Python/sed
# En general, lo más práctico es restaurar la BD en un servidor de staging
# y luego extraer solo la tabla que necesitas
MySQL: backup con credenciales en archivo de configuración
Escribir la contraseña en el comando o en scripts es un riesgo de seguridad. La forma correcta en MySQL es usar un archivo de opciones.
# Crear el archivo de configuración para backups automáticos
# ~/.my.cnf (solo accesible por el usuario actual)
cat > ~/.my.cnf << 'EOF'
[mysqldump]
user=backup_user
password=tu_contraseña_segura
host=localhost
[mysql]
user=backup_user
password=tu_contraseña_segura
host=localhost
EOF
# Proteger el archivo (solo el propietario puede leerlo)
chmod 600 ~/.my.cnf
# Ahora mysqldump lee las credenciales del archivo automáticamente
mysqldump mi_app_db > backup.sql # sin -u ni -p
# Crear un usuario específico para backups con los mínimos permisos necesarios
mysql -u root -p << 'EOF'
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'contraseña_muy_segura';
GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT ON *.* TO 'backup_user'@'localhost';
FLUSH PRIVILEGES;
EOF
PostgreSQL: backup con pg_dump
Backup básico de una base de datos
# Backup en formato SQL plano (el más portable)
pg_dump -U usuario -d nombre_base_datos -f backup.sql
# Si el usuario es postgres (el superusuario por defecto):
pg_dump -U postgres -d mi_app_db -f backup_$(date +%Y%m%d).sql
# Pedir contraseña interactivamente
pg_dump -U postgres -W -d mi_app_db -f backup.sql
# Backup de un servidor remoto
pg_dump -h servidor.ejemplo.com -U usuario -d mi_app_db -f backup.sql
# Ver el contenido del backup
head -50 backup.sql
Formatos de salida en pg_dump
A diferencia de mysqldump, pg_dump ofrece múltiples formatos de salida con diferentes características:
# Formato SQL plano (por defecto con -f): portable, legible con cualquier editor
pg_dump -U postgres -d mi_app_db -f backup.sql
# Restaurar con: psql -U postgres -d mi_app_db -f backup.sql
# Formato personalizado (-Fc): comprimido, permite restauración selectiva con pg_restore
pg_dump -U postgres -Fc -d mi_app_db -f backup.dump
# Más compacto y flexible que el SQL plano
# Formato directorio (-Fd): útil para backups paralelos (tablas en paralelo)
pg_dump -U postgres -Fd -d mi_app_db -f directorio_backup/ -j 4
# -j 4: usar 4 trabajos en paralelo (más rápido en bases de datos grandes)
# Formato tar (-Ft): archivado pero sin compresión nativa
pg_dump -U postgres -Ft -d mi_app_db -f backup.tar
Opciones útiles de pg_dump
# Solo la estructura (esquema), sin datos
pg_dump -U postgres -s -d mi_app_db -f estructura.sql
# Solo los datos, sin estructura
pg_dump -U postgres -a -d mi_app_db -f datos.sql
# Excluir tablas específicas del backup
pg_dump -U postgres -d mi_app_db -T logs_sesiones -T cache_temporal -f backup.sql
# Incluir solo ciertas tablas
pg_dump -U postgres -d mi_app_db -t clientes -t pedidos -f tablas_criticas.sql
# Con compresión (formato personalizado incluye compresión, SQL plano no)
pg_dump -U postgres -Fc -Z 9 -d mi_app_db -f backup.dump # nivel de compresión 9 (máximo)
# Backup de todas las bases de datos (incluye roles y configuración global)
pg_dumpall -U postgres -f backup_completo_servidor.sql
Restaurar un backup en PostgreSQL
# Crear la base de datos si no existe
createdb -U postgres mi_app_db
# O desde psql:
psql -U postgres -c "CREATE DATABASE mi_app_db;"
# Restaurar formato SQL plano con psql
psql -U postgres -d mi_app_db -f backup.sql
# Restaurar formato personalizado con pg_restore
pg_restore -U postgres -d mi_app_db backup.dump
# Restaurar formato personalizado con opciones adicionales
pg_restore -U postgres \
-d mi_app_db \
--clean \ # eliminar objetos existentes antes de restaurar
--if-exists \ # no fallar si el objeto no existe al intentar eliminar
backup.dump
# Ver el contenido de un backup formato personalizado sin restaurar
pg_restore -l backup.dump
# Restaurar solo una tabla específica del backup
pg_restore -U postgres -d mi_app_db -t clientes backup.dump
# Restaurar solo el esquema (sin datos)
pg_restore -U postgres -d mi_app_db -s backup.dump
# Restaurar en paralelo (para bases de datos grandes)
pg_restore -U postgres -d mi_app_db -j 4 directorio_backup/
# Ver errores sin abortar (útil cuando hay conflictos parciales)
pg_restore -U postgres -d mi_app_db --no-privileges backup.dump 2> errores.log
Credenciales sin contraseña en PostgreSQL
# Archivo .pgpass: guarda credenciales de forma segura
# Formato: host:puerto:base_datos:usuario:contraseña
cat > ~/.pgpass << 'EOF'
localhost:5432:mi_app_db:backup_user:contraseña_segura
localhost:5432:*:postgres:contraseña_root
EOF
chmod 600 ~/.pgpass # solo el propietario puede leerlo
# Ahora pg_dump no pide contraseña cuando las credenciales coinciden
pg_dump -U backup_user -d mi_app_db -f backup.sql
# Variable de entorno PGPASSWORD (menos seguro pero útil en scripts)
PGPASSWORD='mi_contraseña' pg_dump -U postgres -d mi_app_db -f backup.sql
Automatizar los backups con cron
Script de backup para MySQL
#!/bin/bash
# /usr/local/bin/backup_mysql.sh
# Configuración
DB_USER="backup_user"
DB_NAME="mi_app_db"
BACKUP_DIR="/var/backups/mysql"
RETENTION_DAYS=30 # mantener backups de los últimos 30 días
FECHA=$(date +%Y%m%d_%H%M%S)
ARCHIVO="$BACKUP_DIR/${DB_NAME}_${FECHA}.sql.gz"
LOG_FILE="/var/log/backup_mysql.log"
# Crear el directorio si no existe
mkdir -p "$BACKUP_DIR"
echo "[$(date)] Iniciando backup de $DB_NAME..." >> "$LOG_FILE"
# Realizar el backup con compresión
mysqldump \
--single-transaction \
--routines \ # incluir procedimientos almacenados y funciones
--triggers \ # incluir triggers
--events \ # incluir eventos programados
"$DB_NAME" | gzip > "$ARCHIVO"
# Verificar que el backup se generó correctamente
if [ $? -eq 0 ] && [ -s "$ARCHIVO" ]; then
TAMANO=$(du -sh "$ARCHIVO" | cut -f1)
echo "[$(date)] Backup completado: $ARCHIVO ($TAMANO)" >> "$LOG_FILE"
else
echo "[$(date)] ERROR: el backup falló" >> "$LOG_FILE"
exit 1
fi
# Eliminar backups más antiguos que RETENTION_DAYS días
find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime +$RETENTION_DAYS -delete
echo "[$(date)] Backups antiguos eliminados (más de $RETENTION_DAYS días)" >> "$LOG_FILE"
# Dar permisos de ejecución al script
chmod +x /usr/local/bin/backup_mysql.sh
# Programar con cron: backup diario a las 2:00 AM
crontab -e
# Añadir esta línea:
# 0 2 * * * /usr/local/bin/backup_mysql.sh
# Backup semanal completo los domingos a las 3:00 AM, diario incremental el resto
# 0 3 * * 0 /usr/local/bin/backup_mysql_completo.sh # domingos
# 0 2 * * 1-6 /usr/local/bin/backup_mysql_incremental.sh # lunes a sábado
Script de backup para PostgreSQL
#!/bin/bash
# /usr/local/bin/backup_postgres.sh
DB_USER="postgres"
DB_NAME="mi_app_db"
BACKUP_DIR="/var/backups/postgres"
RETENTION_DAYS=30
FECHA=$(date +%Y%m%d_%H%M%S)
ARCHIVO="$BACKUP_DIR/${DB_NAME}_${FECHA}.dump"
LOG_FILE="/var/log/backup_postgres.log"
mkdir -p "$BACKUP_DIR"
echo "[$(date)] Iniciando backup de $DB_NAME..." >> "$LOG_FILE"
# Backup en formato personalizado (comprimido y permite restauración selectiva)
pg_dump \
-U "$DB_USER" \
-Fc \
-Z 6 \ # nivel de compresión 6 (balance entre velocidad y tamaño)
-d "$DB_NAME" \
-f "$ARCHIVO"
if [ $? -eq 0 ] && [ -s "$ARCHIVO" ]; then
TAMANO=$(du -sh "$ARCHIVO" | cut -f1)
echo "[$(date)] Backup completado: $ARCHIVO ($TAMANO)" >> "$LOG_FILE"
else
echo "[$(date)] ERROR: el backup falló" >> "$LOG_FILE"
exit 1
fi
# Limpiar backups antiguos
find "$BACKUP_DIR" -name "${DB_NAME}_*.dump" -mtime +$RETENTION_DAYS -delete
echo "[$(date)] Limpieza completada" >> "$LOG_FILE"
Copiar backups a almacenamiento externo
Un backup en el mismo servidor que la base de datos no te protege si el servidor falla. Los backups deben estar en al menos una ubicación externa.
# Copiar a otro servidor con rsync
rsync -avz --delete /var/backups/mysql/ usuario@backup-server.ejemplo.com:/backups/mysql/
# Subir a Amazon S3 (requiere AWS CLI instalado y configurado)
aws s3 cp "$ARCHIVO" s3://mi-bucket-backups/mysql/
aws s3 sync /var/backups/mysql/ s3://mi-bucket-backups/mysql/ --delete
# Subir a Google Cloud Storage
gsutil cp "$ARCHIVO" gs://mi-bucket-backups/mysql/
gsutil rsync -d /var/backups/mysql/ gs://mi-bucket-backups/mysql/
# Añadir al script de backup después de verificar que se creó correctamente:
if [ $? -eq 0 ]; then
aws s3 cp "$ARCHIVO" "s3://mi-bucket-backups/${DB_NAME}/" && \
echo "[$(date)] Backup subido a S3" >> "$LOG_FILE"
fi
Verificar que los backups funcionan: el test de restauración
Un backup que nunca has probado restaurar no es un backup: es un archivo del que no sabes si sirve. Prueba restaurar periódicamente en un entorno de prueba.
# Test de restauración en MySQL: restaurar en una base de datos de prueba
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS mi_app_db_test;"
mysql -u root -p mi_app_db_test < backup.sql
# Verificar que los datos están bien
mysql -u root -p mi_app_db_test -e "
SELECT 'clientes' AS tabla, COUNT(*) AS filas FROM clientes
UNION ALL
SELECT 'pedidos', COUNT(*) FROM pedidos
UNION ALL
SELECT 'productos', COUNT(*) FROM productos;
"
# Limpiar la base de datos de prueba
mysql -u root -p -e "DROP DATABASE mi_app_db_test;"
# Test de restauración en PostgreSQL
createdb -U postgres mi_app_db_test
pg_restore -U postgres -d mi_app_db_test backup.dump
# Verificar datos
psql -U postgres -d mi_app_db_test -c "
SELECT 'clientes' AS tabla, COUNT(*) AS filas FROM clientes
UNION ALL
SELECT 'pedidos', COUNT(*) FROM pedidos
UNION ALL
SELECT 'productos', COUNT(*) FROM productos;
"
# Limpiar
dropdb -U postgres mi_app_db_test
# Script de verificación automática que se puede añadir al cron mensual
#!/bin/bash
# /usr/local/bin/verificar_backup.sh
ULTIMO_BACKUP=$(ls -t /var/backups/postgres/*.dump | head -1)
TEST_DB="verificacion_backup_$(date +%s)"
echo "Verificando backup: $ULTIMO_BACKUP"
createdb -U postgres "$TEST_DB"
pg_restore -U postgres -d "$TEST_DB" "$ULTIMO_BACKUP"
if [ $? -eq 0 ]; then
FILAS=$(psql -U postgres -d "$TEST_DB" -t -c "SELECT COUNT(*) FROM clientes;")
echo "Backup verificado: $FILAS clientes encontrados"
else
echo "ERROR: la restauración del backup falló"
fi
dropdb -U postgres "$TEST_DB"
Opciones para bases de datos grandes
# Para bases de datos de varios GB, las opciones básicas pueden ser lentas
# Estas alternativas mejoran el rendimiento:
# MySQL: mydumper (backup paralelo, mucho más rápido que mysqldump)
# apt install mydumper
mydumper \
--user=root \
--password=contraseña \
--database=mi_app_db \
--outputdir=/var/backups/mysql/$(date +%Y%m%d)/ \
--threads=4 \
--compress \
--trx-consistency-only
# Restaurar con myloader
myloader \
--user=root \
--password=contraseña \
--database=mi_app_db \
--directory=/var/backups/mysql/20260615/ \
--threads=4
# PostgreSQL: pg_dump paralelo con formato directorio
pg_dump -U postgres -Fd -j 8 -d mi_app_db -f /var/backups/postgres/$(date +%Y%m%d)/
# Restaurar en paralelo
pg_restore -U postgres -d mi_app_db -j 8 /var/backups/postgres/20260615/
Point-in-time recovery: recuperar el estado exacto en un momento
Los backups periódicos te permiten restaurar al punto en el que se hizo el backup. Si el backup es de las 2 AM y el desastre ocurre a las 11 AM, pierdes 9 horas de datos. Para recuperar hasta el minuto exacto del fallo, necesitas WAL archiving en PostgreSQL o binary log en MySQL.
-- PostgreSQL: activar WAL archiving (en postgresql.conf)
-- wal_level = replica
-- archive_mode = on
-- archive_command = 'cp %p /var/lib/postgresql/wal_archive/%f'
-- MySQL: activar binary log (en my.cnf)
-- [mysqld]
-- log_bin = /var/log/mysql/mysql-bin
-- binlog_format = ROW
-- expire_logs_days = 7
-- Con binary log activo, para restaurar hasta las 11:00:00 del 15 de junio:
-- 1. Restaurar el último backup completo
mysql -u root -p mi_app_db < backup_20260615_020000.sql
-- 2. Aplicar los binary logs desde el backup hasta el momento del fallo
mysqlbinlog \
--start-datetime="2026-06-15 02:00:00" \
--stop-datetime="2026-06-15 10:59:59" \
/var/log/mysql/mysql-bin.000001 \
/var/log/mysql/mysql-bin.000002 | mysql -u root -p mi_app_db
Resumen
- En MySQL,
mysqldump --single-transactiongenera un backup consistente sin bloquear la base de datos. Usa| gzippara comprimir ymysqlpara restaurar. - En PostgreSQL,
pg_dump -Fcgenera un backup comprimido y flexible que permite restauración selectiva conpg_restore. El formato directorio (-Fd -j N) permite backups paralelos para bases de datos grandes. - Guarda las credenciales en
~/.my.cnf(MySQL) o~/.pgpass(PostgreSQL) con permisos 600, nunca en los scripts directamente. - Automatiza los backups con cron y elimina los más antiguos automáticamente con
find -mtime. - Copia los backups a almacenamiento externo (otro servidor, S3, GCS): un backup en el mismo servidor no te protege si el servidor falla.
- Prueba la restauración periódicamente en un entorno de prueba. Un backup no verificado no es fiable.
- Para recuperación punto a punto (sin perder datos hasta el minuto del fallo), activa el binary log en MySQL o el WAL archiving en PostgreSQL.
No hay comentarios todavía. Sé el primero en compartir tu opinión.