VPS 備份最麻煩的不是寫出 tar 指令,而是讓它真的定時執行、檔案不會放滿磁碟、需要時還還得回來。這篇會把「可用的備份流程」該怎麼設計講清楚。
你如果不想每次都只是照著別人的畫面一步一步跟著按,卻不知道自己到底改了什麼,這篇會比較適合你。重點不是讓你背流程,而是讓你下次遇到類似情況時,還知道該怎麼自己判斷。
你會學到什麼
- 知道網站、資料庫與系統檔該怎麼拆開備份。
- 會做基本排程與異地保存。
- 理解備份完成後還要驗證與輪替,不是存了就算。
- 學會備份加密與監控告警機制。
什麼情況最適合先看這篇
- 第一次自己管理 Linux 主機,想把操作做穩而不是只會貼指令的人
- 你現在正要處理「VPS 自動備份設定」這類操作
- 你希望做完之後不只功能能用,連驗證與排錯也有概念
開始前先確認
- 先列出哪些資料不能丟。
- 準備好備份目的地,例如另一台主機、物件儲存或雲端硬碟。
- 確認 VPS 剩餘磁碟空間是否足夠。
先提醒你一件事
VPS 最怕的不是不會下指令,而是你不知道指令會影響到哪個服務、哪個設定檔、哪個對外連線。
詳細教學與操作步驟
定期備份是保護資料最重要的措施。本文說明如何撰寫備份腳本、設定排程自動執行,以及使用 rclone 將備份同步至遠端儲存空間。
建立備份腳本
-
建立備份目錄與腳本:
mkdir -p /opt/backups nano /opt/backups/backup.sh -
寫入以下內容:
#!/bin/bash # ============================================ # 自動備份腳本 — 網站檔案 + MySQL 資料庫 # ============================================ # 設定 BACKUP_DIR="/opt/backups" DATE=$(date +%Y%m%d_%H%M%S) KEEP_DAYS=7 LOG_FILE="${BACKUP_DIR}/backup.log" # MySQL 設定(建議改用 ~/.my.cnf 儲存帳密) DB_NAME="your_database" DB_USER="your_db_user" DB_PASS="your_db_password" # 備份網站檔案 echo "[$(date)] 開始備份網站檔案..." >> ${LOG_FILE} tar -czf ${BACKUP_DIR}/web_${DATE}.tar.gz /var/www/ 2>/dev/null if [ $? -eq 0 ]; then echo "[$(date)] 網站備份完成" >> ${LOG_FILE} else echo "[$(date)] [ERROR] 網站備份失敗!" >> ${LOG_FILE} fi # 備份資料庫 echo "[$(date)] 開始備份資料庫..." >> ${LOG_FILE} mysqldump -u${DB_USER} -p${DB_PASS} --routines --triggers ${DB_NAME} | \ gzip > ${BACKUP_DIR}/db_${DATE}.sql.gz if [ $? -eq 0 ]; then echo "[$(date)] 資料庫備份完成" >> ${LOG_FILE} else echo "[$(date)] [ERROR] 資料庫備份失敗!" >> ${LOG_FILE} fi # 備份設定檔 echo "[$(date)] 備份系統設定檔..." >> ${LOG_FILE} tar -czf ${BACKUP_DIR}/config_${DATE}.tar.gz \ /etc/nginx/ /etc/php/ /etc/mysql/ /etc/crontab 2>/dev/null # 刪除超過保留天數的舊備份 echo "[$(date)] 清理 ${KEEP_DAYS} 天前的舊備份..." >> ${LOG_FILE} find ${BACKUP_DIR} -name "web_*.tar.gz" -mtime +${KEEP_DAYS} -delete find ${BACKUP_DIR} -name "db_*.sql.gz" -mtime +${KEEP_DAYS} -delete find ${BACKUP_DIR} -name "config_*.tar.gz" -mtime +${KEEP_DAYS} -delete # 顯示備份結果 echo "[$(date)] 備份完成!目前備份檔案:" >> ${LOG_FILE} ls -lh ${BACKUP_DIR}/*.gz >> ${LOG_FILE} 2>/dev/null echo "---" >> ${LOG_FILE} -
設定腳本執行權限:
chmod +x /opt/backups/backup.sh -
手動測試執行:
/opt/backups/backup.sh
更安全的資料庫帳密管理
把資料庫帳密寫在腳本裡不太安全。建議改用 MySQL 的設定檔:
# 建立 ~/.my.cnf
nano ~/.my.cnf
寫入:
[mysqldump]
user=your_db_user
password=your_db_password
設定權限(只有 root 能讀):
chmod 600 ~/.my.cnf
然後腳本裡的 mysqldump 就可以改成不帶帳密:
mysqldump --routines --triggers ${DB_NAME} | gzip > ${BACKUP_DIR}/db_${DATE}.sql.gz
備份加密
如果備份要傳到雲端,建議先加密:
# 用 GPG 對稱加密
gpg --symmetric --cipher-algo AES256 \
--output ${BACKUP_DIR}/db_${DATE}.sql.gz.gpg \
${BACKUP_DIR}/db_${DATE}.sql.gz
# 加密完可以刪除未加密版本
rm ${BACKUP_DIR}/db_${DATE}.sql.gz
# 還原時解密
gpg --decrypt db_20260410.sql.gz.gpg > db_20260410.sql.gz
設定 Cron 排程
使用 crontab 設定每天凌晨 3 點自動執行備份:
-
編輯 crontab:
crontab -e -
在最後加入以下行:
0 3 * * * /opt/backups/backup.sh >> /opt/backups/backup.log 2>&1 -
確認排程已設定:
crontab -l
Cron 時間格式說明:分 時 日 月 星期,0 3 * * * 代表每天凌晨 3:00。
常用排程範例:
# 每 6 小時備份一次
0 */6 * * * /opt/backups/backup.sh
# 每週日凌晨 2 點做完整備份
0 2 * * 0 /opt/backups/full-backup.sh
# 每月 1 號做月度備份(保留更久)
0 1 1 * * /opt/backups/monthly-backup.sh
使用 rclone 同步至遠端
僅在本機備份不夠安全,建議使用 rclone 將備份同步至遠端儲存(如 S3、Google Drive、Backblaze B2 等)。
-
安裝 rclone:
curl https://rclone.org/install.sh | bash -
設定遠端儲存空間:
rclone config依照互動式引導選擇儲存服務(例如 S3、Google Drive),並完成授權。
-
測試同步:
# 將備份目錄同步至遠端(remote-name 為您在 rclone config 中設定的名稱) rclone sync /opt/backups remote-name:backup-bucket/vps-backups -
加入備份腳本中,在備份完成後自動同步:
# 在 backup.sh 最後加入 echo "[$(date)] 開始同步至遠端..." >> ${LOG_FILE} rclone sync ${BACKUP_DIR} remote-name:backup-bucket/vps-backups \ --transfers 4 --checkers 8 \ --log-file=${BACKUP_DIR}/rclone.log --log-level INFO echo "[$(date)] 遠端同步完成!" >> ${LOG_FILE}
rclone 進階設定
限制頻寬避免影響正式服務:
rclone sync /opt/backups remote-name:bucket/backups \
--bwlimit 10M # 限制上傳速度 10MB/s
只保留遠端最近 30 天的備份:
# 刪除遠端超過 30 天的檔案
rclone delete remote-name:bucket/backups --min-age 30d
驗證備份可還原
備份只有在能成功還原時才有價值。建議定期測試還原流程:
-
還原網站檔案(至測試目錄):
mkdir -p /tmp/restore-test tar -xzf /opt/backups/web_最新日期.tar.gz -C /tmp/restore-test/ # 確認檔案數量和大小合理 find /tmp/restore-test -type f | wc -l du -sh /tmp/restore-test/ -
還原資料庫(至測試資料庫):
mysql -u root -p -e "CREATE DATABASE restore_test;" gunzip < /opt/backups/db_最新日期.sql.gz | mysql -u root -p restore_test # 確認資料表數量 mysql -u root -p -e "SHOW TABLES;" restore_test | wc -l # 抽查關鍵資料表的筆數 mysql -u root -p -e "SELECT COUNT(*) FROM wp_posts;" restore_test -
確認資料完整後,清除測試資料:
rm -rf /tmp/restore-test mysql -u root -p -e "DROP DATABASE restore_test;"
**重要提醒:**至少每季測試一次還原流程,確保備份確實可用。
備份失敗告警
備份腳本跑了但失敗,如果你不知道,等於沒有備份。加一個簡單的告警機制:
# 在 backup.sh 最後加入
BACKUP_SIZE=$(du -sb ${BACKUP_DIR}/web_${DATE}.tar.gz 2>/dev/null | cut -f1)
# 如果備份檔案小於 1MB,可能有問題
if [ -z "$BACKUP_SIZE" ] || [ "$BACKUP_SIZE" -lt 1048576 ]; then
echo "WARNING: 備份檔案異常小或不存在" | \
mail -s "[VPS 備份警報] $(hostname) 備份可能失敗" your@email.com
fi
如果你的 VPS 沒有設定 mail,可以改用 webhook 通知(例如 Slack 或 Discord):
# 用 curl 發送 Discord webhook 通知
if [ "$BACKUP_FAILED" = true ]; then
curl -H "Content-Type: application/json" \
-d "{\"content\":\"[警報] $(hostname) 備份失敗!時間:$(date)\"}" \
"https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
fi
備份策略建議
| 資料類型 | 備份頻率 | 保留時間 | 說明 |
|---|---|---|---|
| 資料庫 | 每 6 小時 | 7 天 | 資料變動最頻繁 |
| 網站檔案 | 每天 | 14 天 | 檔案變動較少 |
| 系統設定 | 每週 | 30 天 | 變動最少 |
| 完整快照 | 每月 | 90 天 | 災難復原用 |
做完後怎麼確認自己真的有設對
- 用
crontab -l確認排程已設定。 - 手動跑一次腳本,檢查 log 沒有錯誤。
- 確認備份檔案大小合理(不是 0 bytes 也不是異常小)。
- 測試從遠端下載備份檔案回來,確認可以解壓。
- 如果有加密,確認你記得解密密碼或有保存金鑰。
這一題最常踩的坑
- 備份和正式資料放在同一台主機同一顆磁碟。
- 只設定排程不看結果,失敗很久都不知道。
- 從來不做還原演練,真的出事才發現流程斷掉。
- 備份檔沒有加密就直接丟到雲端。
- 沒有做備份輪替,磁碟被備份檔塞滿導致服務掛掉。
如果你要往下一步走
如果你接下來要搬站,建議接著看 網站搬家教學。備份做好後,也該做好安全防護,參考 Fail2Ban 防護教學。如果你準備開始自己架服務,可以直接對照侃瑞的 VPS 方案與價格 選環境。