Cách backup website tự động là một trong những kỹ năng cơ bản nhưng cực kỳ quan trọng cho bất kỳ ai quản lý website. Bài viết này hướng dẫn từng bước setup hệ thống backup tự động hoàn chỉnh — database + files + sync cloud — miễn phí, chạy tự động mỗi ngày, không cần phần mềm trả phí. Tổng thời gian setup: khoảng 30–45 phút.
Lead magnet: Tải miễn phí Backup Strategy Template cho SME — template lập kế hoạch backup + script mẫu sẵn dùng.
Tổng quan hệ thống backup
Mục tiêu
| Yêu cầu | Giải pháp |
|---|---|
| Backup database hàng ngày | mysqldump + cron |
| Backup files hàng ngày | tar/rsync + cron |
| Giữ 30 bản backup local | Script cleanup tự động |
| Sync offsite (cloud) | rclone → Google Drive/S3/B2 |
| Alert khi backup lỗi | Script gửi Telegram |
| Chi phí | 0 VND (tools miễn phí + free cloud storage) |
Kiến trúc
[Cron Job - 3:00 AM hàng ngày]
├── mysqldump → backup database → gzip → /opt/backup/db/
├── tar → backup website files → gzip → /opt/backup/files/
├── rclone sync → Google Drive / S3 / Backblaze B2
├── Cleanup backup > 30 ngày
└── Gửi notification → Telegram
Phần 1: Backup Database (MySQL/MariaDB)
Bước 1: Tạo MySQL user cho backup
Không dùng root để backup — tạo user riêng với quyền tối thiểu:
sudo mysql -u root -p
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'BackupStrongPassword!@#2026';
GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT ON *.* TO 'backup_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Bước 2: Tạo credential file (không lưu password trong script)
sudo tee /root/.my-backup.cnf > /dev/null <<EOF
[client]
user=backup_user
password=BackupStrongPassword!@#2026
EOF
sudo chmod 600 /root/.my-backup.cnf
Bước 3: Script backup database
sudo tee /opt/scripts/backup-db.sh > /dev/null <<'SCRIPT'
#!/bin/bash
#
# Database Backup Script
# Chạy hàng ngày bằng crontab
#
# Cấu hình
BACKUP_DIR="/opt/backup/db"
MYSQL_CONF="/root/.my-backup.cnf"
DATABASES="website_db" # Thêm database khác: "db1 db2 db3"
RETENTION_DAYS=30
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/log/backup-db.log"
# Tạo thư mục
mkdir -p $BACKUP_DIR
echo "[$DATE] Starting database backup..." >> $LOG_FILE
# Backup từng database
for DB in $DATABASES; do
BACKUP_FILE="$BACKUP_DIR/${DB}_${DATE}.sql.gz"
mysqldump --defaults-extra-file=$MYSQL_CONF \
--single-transaction \
--quick \
--lock-tables=false \
--routines \
--triggers \
--events \
$DB | gzip > $BACKUP_FILE
if [ $? -eq 0 ]; then
SIZE=$(du -h $BACKUP_FILE | cut -f1)
echo "[$DATE] SUCCESS: $DB → $BACKUP_FILE ($SIZE)" >> $LOG_FILE
else
echo "[$DATE] ERROR: Failed to backup $DB" >> $LOG_FILE
# Gửi alert (xem phần Telegram notification)
fi
done
# Xóa backup cũ hơn 30 ngày
find $BACKUP_DIR -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
echo "[$DATE] Cleaned up backups older than $RETENTION_DAYS days" >> $LOG_FILE
echo "[$DATE] Database backup completed" >> $LOG_FILE
SCRIPT
sudo chmod +x /opt/scripts/backup-db.sh
Bước 4: Test backup
sudo /opt/scripts/backup-db.sh
# Kiểm tra file backup
ls -la /opt/backup/db/
# Kiểm tra backup file hợp lệ
gunzip -t /opt/backup/db/website_db_*.sql.gz
echo $? # 0 = OK
Bước 5: Test restore
# Tạo test database
mysql -u root -p -e "CREATE DATABASE test_restore;"
# Restore backup vào test database
gunzip -c /opt/backup/db/website_db_LATEST.sql.gz | mysql -u root -p test_restore
# Verify
mysql -u root -p -e "SELECT COUNT(*) FROM test_restore.orders;"
# Cleanup
mysql -u root -p -e "DROP DATABASE test_restore;"
Phần 2: Backup Website Files
Script backup files
sudo tee /opt/scripts/backup-files.sh > /dev/null <<'SCRIPT'
#!/bin/bash
#
# Files Backup Script
#
BACKUP_DIR="/opt/backup/files"
WEB_DIR="/var/www"
RETENTION_DAYS=14 # Files lớn hơn DB, giữ 14 ngày
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/log/backup-files.log"
mkdir -p $BACKUP_DIR
echo "[$DATE] Starting files backup..." >> $LOG_FILE
# Backup website files
BACKUP_FILE="$BACKUP_DIR/website_${DATE}.tar.gz"
tar czf $BACKUP_FILE \
--exclude='*.log' \
--exclude='node_modules' \
--exclude='.git' \
--exclude='cache/*' \
$WEB_DIR
if [ $? -eq 0 ]; then
SIZE=$(du -h $BACKUP_FILE | cut -f1)
echo "[$DATE] SUCCESS: Files → $BACKUP_FILE ($SIZE)" >> $LOG_FILE
else
echo "[$DATE] ERROR: Failed to backup files" >> $LOG_FILE
fi
# Cleanup
find $BACKUP_DIR -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
echo "[$DATE] Files backup completed" >> $LOG_FILE
SCRIPT
sudo chmod +x /opt/scripts/backup-files.sh
Backup config files riêng
Config files quan trọng nhưng ít thay đổi — backup riêng:
sudo tee /opt/scripts/backup-config.sh > /dev/null <<'SCRIPT'
#!/bin/bash
BACKUP_DIR="/opt/backup/config"
DATE=$(date +%Y%m%d)
mkdir -p $BACKUP_DIR
tar czf $BACKUP_DIR/config_${DATE}.tar.gz \
/etc/nginx/ \
/etc/php/ \
/etc/mysql/ \
/etc/letsencrypt/ \
/etc/ssh/sshd_config \
/etc/crontab \
/opt/scripts/ \
2>/dev/null
# Giữ 90 ngày (config ít thay đổi)
find $BACKUP_DIR -name "*.tar.gz" -mtime +90 -delete
SCRIPT
sudo chmod +x /opt/scripts/backup-config.sh
Phần 3: Sync lên Cloud (Offsite Backup)
Cài rclone
curl https://rclone.org/install.sh | sudo bash
Cấu hình rclone với Google Drive
rclone config
Theo hướng dẫn:
n— New remote- Name:
gdrive - Storage:
drive(Google Drive) - Scope:
drive(full access) - Theo dõi hướng dẫn OAuth (mở browser, authorize)
Cấu hình rclone với Backblaze B2 (rẻ hơn)
rclone config
n— New remote- Name:
b2 - Storage:
b2(Backblaze B2) - Account ID:
your-account-id - Application Key:
your-app-key
Script sync offsite
sudo tee /opt/scripts/backup-sync.sh > /dev/null <<'SCRIPT'
#!/bin/bash
#
# Sync backup to cloud storage
#
BACKUP_DIR="/opt/backup"
REMOTE="gdrive:vps-backup" # Hoặc "b2:bucket-name/vps-backup"
LOG_FILE="/var/log/backup-sync.log"
DATE=$(date +%Y%m%d_%H%M%S)
echo "[$DATE] Starting cloud sync..." >> $LOG_FILE
rclone sync $BACKUP_DIR $REMOTE \
--transfers 4 \
--checkers 8 \
--log-file=$LOG_FILE \
--log-level INFO
if [ $? -eq 0 ]; then
echo "[$DATE] SUCCESS: Synced to cloud" >> $LOG_FILE
else
echo "[$DATE] ERROR: Cloud sync failed" >> $LOG_FILE
fi
echo "[$DATE] Cloud sync completed" >> $LOG_FILE
SCRIPT
sudo chmod +x /opt/scripts/backup-sync.sh
Phần 4: Telegram Notification
Tạo notification script
sudo tee /opt/scripts/backup-notify.sh > /dev/null <<'SCRIPT'
#!/bin/bash
#
# Telegram notification for backup status
#
BOT_TOKEN="YOUR_TELEGRAM_BOT_TOKEN"
CHAT_ID="YOUR_CHAT_ID"
send_telegram() {
local message=$1
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-d "chat_id=${CHAT_ID}" \
-d "text=${message}" \
-d "parse_mode=HTML" > /dev/null
}
# Kiểm tra kết quả backup
DB_BACKUP=$(ls -t /opt/backup/db/*.sql.gz 2>/dev/null | head -1)
FILES_BACKUP=$(ls -t /opt/backup/files/*.tar.gz 2>/dev/null | head -1)
if [ -n "$DB_BACKUP" ] && [ -n "$FILES_BACKUP" ]; then
DB_SIZE=$(du -h $DB_BACKUP | cut -f1)
FILES_SIZE=$(du -h $FILES_BACKUP | cut -f1)
TOTAL_BACKUPS_DB=$(ls /opt/backup/db/*.sql.gz 2>/dev/null | wc -l)
TOTAL_BACKUPS_FILES=$(ls /opt/backup/files/*.tar.gz 2>/dev/null | wc -l)
DISK_USAGE=$(df -h /opt/backup | tail -1 | awk '{print $5}')
MESSAGE="<b>Backup Report</b>
Server: $(hostname)
Date: $(date '+%Y-%m-%d %H:%M')
DB: $DB_SIZE ($TOTAL_BACKUPS_DB copies)
Files: $FILES_SIZE ($TOTAL_BACKUPS_FILES copies)
Disk: $DISK_USAGE
Status: OK"
send_telegram "$MESSAGE"
else
MESSAGE="<b>BACKUP FAILED</b>
Server: $(hostname)
Date: $(date '+%Y-%m-%d %H:%M')
Check /var/log/backup-*.log for details"
send_telegram "$MESSAGE"
fi
SCRIPT
sudo chmod +x /opt/scripts/backup-notify.sh
Phần 5: Master Backup Script + Crontab
Master script
sudo tee /opt/scripts/backup-master.sh > /dev/null <<'SCRIPT'
#!/bin/bash
#
# Master Backup Script - chạy tất cả backup jobs
#
LOG_FILE="/var/log/backup-master.log"
DATE=$(date +%Y%m%d_%H%M%S)
echo "========================================" >> $LOG_FILE
echo "[$DATE] BACKUP STARTED" >> $LOG_FILE
# 1. Backup database
/opt/scripts/backup-db.sh
# 2. Backup files
/opt/scripts/backup-files.sh
# 3. Backup config (chỉ chạy thứ 2 hàng tuần)
if [ $(date +%u) -eq 1 ]; then
/opt/scripts/backup-config.sh
fi
# 4. Sync to cloud
/opt/scripts/backup-sync.sh
# 5. Send notification
/opt/scripts/backup-notify.sh
echo "[$DATE] BACKUP COMPLETED" >> $LOG_FILE
echo "========================================" >> $LOG_FILE
SCRIPT
sudo chmod +x /opt/scripts/backup-master.sh
Cấu hình crontab
sudo crontab -e
Thêm:
# Backup hàng ngày lúc 3:00 AM
0 3 * * * /opt/scripts/backup-master.sh >> /var/log/backup-master.log 2>&1
# Rotate log files hàng tháng
0 0 1 * * find /var/log/backup-*.log -size +10M -exec truncate -s 0 {} \;
Phần 6: Verify & Test Restore
Script test restore tự động
sudo tee /opt/scripts/backup-verify.sh > /dev/null <<'SCRIPT'
#!/bin/bash
#
# Verify backup integrity
# Chạy hàng tuần
#
LOG_FILE="/var/log/backup-verify.log"
DATE=$(date +%Y%m%d_%H%M%S)
echo "[$DATE] Starting backup verification..." >> $LOG_FILE
# Test database backup integrity
LATEST_DB=$(ls -t /opt/backup/db/*.sql.gz | head -1)
if gunzip -t $LATEST_DB 2>/dev/null; then
echo "[$DATE] DB backup integrity: OK" >> $LOG_FILE
else
echo "[$DATE] DB backup integrity: FAILED!" >> $LOG_FILE
fi
# Test files backup integrity
LATEST_FILES=$(ls -t /opt/backup/files/*.tar.gz | head -1)
if tar tzf $LATEST_FILES > /dev/null 2>&1; then
echo "[$DATE] Files backup integrity: OK" >> $LOG_FILE
else
echo "[$DATE] Files backup integrity: FAILED!" >> $LOG_FILE
fi
# Test restore database (tạo temp database)
mysql -u root -e "CREATE DATABASE IF NOT EXISTS backup_verify_test;" 2>/dev/null
gunzip -c $LATEST_DB | mysql -u root backup_verify_test 2>/dev/null
if [ $? -eq 0 ]; then
TABLES=$(mysql -u root -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='backup_verify_test';")
echo "[$DATE] DB restore test: OK ($TABLES tables)" >> $LOG_FILE
else
echo "[$DATE] DB restore test: FAILED!" >> $LOG_FILE
fi
mysql -u root -e "DROP DATABASE IF EXISTS backup_verify_test;" 2>/dev/null
echo "[$DATE] Backup verification completed" >> $LOG_FILE
SCRIPT
sudo chmod +x /opt/scripts/backup-verify.sh
# Chạy hàng tuần (Chủ Nhật 4:00 AM)
(sudo crontab -l; echo "0 4 * * 0 /opt/scripts/backup-verify.sh >> /var/log/backup-verify.log 2>&1") | sudo crontab -
Tổng kết: Checklist setup
| # | Bước | Thời gian | Kiểm tra |
|---|---|---|---|
| 1 | Tạo MySQL backup user | 5 phút | ☐ |
| 2 | Tạo credential file (.my-backup.cnf) | 2 phút | ☐ |
| 3 | Tạo script backup database | 5 phút | ☐ |
| 4 | Tạo script backup files | 5 phút | ☐ |
| 5 | Cài rclone + cấu hình cloud | 10 phút | ☐ |
| 6 | Tạo script cloud sync | 3 phút | ☐ |
| 7 | Setup Telegram notification | 5 phút | ☐ |
| 8 | Tạo master script | 3 phút | ☐ |
| 9 | Cấu hình crontab | 2 phút | ☐ |
| 10 | Test chạy thủ công | 5 phút | ☐ |
| 11 | Test restore | 10 phút | ☐ |
| Tổng | ~55 phút |
FAQ — Câu hỏi thường gặp
Backup nên chạy lúc mấy giờ?
2:00–4:00 AM — traffic thấp nhất, ít ảnh hưởng performance. Tránh chạy lúc 0:00 (nhiều cron job khác cũng chạy). Nếu có nhiều server, stagger thời gian backup (server 1 lúc 2:00, server 2 lúc 2:30).
Backup tốn bao nhiêu dung lượng?
Database 1GB → backup gzip ~200MB/ngày × 30 ngày = ~6GB. Files 5GB → backup gzip ~2GB/tuần × 4 tuần = ~8GB. Tổng ~14GB cho 30 ngày retention. Chi phí lưu trữ cloud: ~2.000 VND/tháng (Backblaze B2).
rclone có an toàn không?
rclone là open source, được sử dụng rộng rãi. Data transfer được encrypt (HTTPS). Có thể thêm encryption cho data at rest: rclone sync --crypt-remote. Credential file nên chmod 600 và chỉ root access.
Kết luận
Setup backup tự động miễn phí mất chưa đến 1 giờ, nhưng bảo vệ dữ liệu trị giá hàng tỉ đồng. Không có lý do để chưa có backup — bắt đầu ngay hôm nay.
Nếu bạn cần hỗ trợ setup backup chuyên nghiệp hoặc dịch vụ xây dựng hệ thống với backup tự động cho doanh nghiệp, hãy liên hệ Trinh Digital.