WordPress 全站自动备份与恢复(2026 实操):数据库加 wp-content 打包,rclone 再传一份异地

磁盘写满 MySQL 起不来,或者主题更新完前台白屏,这种时候你想把昨天的站点拉回来。宝塔的计划任务能定时备份,但默认丢在同一块盘的另一个目录,机器一挂,备份陪着一起走。我现在的做法是 WP-CLI 导数据库、tar 打 wp-content、rclone 推一份到对象存储,整件事写成一个脚本交给 cron,出事了十几分钟能还原。

备份要备什么,不要备什么

WordPress 的文件分两类,能重新下载的和不能的。wp-admin、wp-includes 以及官方仓库里的主题插件,装回同版本就有,没必要占备份空间。真正丢不起的是数据库(文章、评论、用户、设置)、wp-content/uploads 里的所有附件,还有被你自己改过的主题文件,wp-config.php 也得带上,里面有数据库密码。

给你一个空间概念,我这边实测的数字:wp-content 总共 292M,uploads 占 166M,themes 71M,plugins 46M,打包成 tar.gz 是 192M,耗时 15 秒。数据库 gzip 完不到 800K,29 张表。也就是说备份的体积瓶颈从来在图片,不在数据库。

前置条件

一台能 SSH 的服务器,装好 WP-CLI,磁盘留出两倍于 wp-content 的空闲空间,外加一个异地存储。阿里云 OSS、Backblaze B2、Cloudflare R2 都能用,重点是别放在同一台机器上,那不叫异地。

rclone 是单文件程序,一条命令装好,装完 confirm 一下版本:

curl https://rclone.org/install.sh | sudo bash
rclone version

第一步:把数据库导出来

mkdir -p /root/backup
cd /www/wwwroot/littlesheep.cc
wp db export - --allow-root --add-drop-table | gzip -9 > /root/backup/db-$(date +%F).sql.gz

- 表示结果走标准输出直接进 gzip,不落地中间文件。--add-drop-table 让每张表前面带一句 DROP TABLE IF EXISTS,恢复时不会因为表已存在就半路报错。用 root 跑必须加 --allow-root,宝塔里一般装在 /usr/local/bin/wp,写脚本时用绝对路径。

导完别省校验这一步:

gunzip -t /root/backup/db-2026-09-22.sql.gz && echo OK
ls -lh /root/backup/

gunzip -t 只验完整性,没问题就静默返回。文件存在不等于备份有效,0 字节的 dump 也能安安静静躺在那里。

第二步:打包 wp-content

tar --warning=no-file-changed -czf /root/backup/wpcontent-$(date +%F).tar.gz \
  --exclude="wp-content/cache" --exclude="wp-content/upgrade" \
  -C /www/wwwroot/littlesheep.cc wp-content

cache 和 upgrade 是临时目录,排掉能省下不少无效体积,--warning=no-file-changed 是防打包时文件正在被写导致 tar 中途退出。

有个坑得提一下,uploads 如果是软链接(有站长为了把图片放数据盘会这么干),tar 默认只存链接本身,不会跟进去打包真实文件,备份看着成功,恢复时图片全丢。开打之前 ls -l wp-content/uploads 看一眼那个箭头,真遇到软链接就加 -h 让 tar 跟随。

第三步:rclone 配一个异地

S3 兼容接口的对象存储基本都能接,命令行建 remote 不用进交互:

rclone config create oss s3 \
  provider Alibaba \
  access_key_id 你的AK \
  secret_access_key 你的SK \
  endpoint oss-cn-shenzhen.aliyuncs.com
rclone lsd oss:

endpoint 按你的区域填,以存储控制台里显示的为准,换 R2 或者 B2 时改 provider 和 endpoint 就行。lsd 能列出 bucket 就说明通了。

第四步:串成一个脚本

#!/bin/bash
set -euo pipefail

SITE=/www/wwwroot/littlesheep.cc
DIR=/root/backup
KEEP=7
STAMP=$(date +%F)

mkdir -p "$DIR"
cd "$SITE"

/usr/local/bin/wp db export - --allow-root --add-drop-table \
  | gzip -9 > "$DIR/db-$STAMP.sql.gz"

tar --warning=no-file-changed -czf "$DIR/wpcontent-$STAMP.tar.gz" \
  --exclude="wp-content/cache" --exclude="wp-content/upgrade" \
  -C "$SITE" wp-content

gzip -t "$DIR/db-$STAMP.sql.gz"
gzip -t "$DIR/wpcontent-$STAMP.tar.gz"

rclone copy "$DIR" "oss:mybackup/$(hostname)/"
find "$DIR" -name "*.gz" -mtime +$KEEP -delete
echo "$STAMP backup ok"

set -euo pipefail 是这脚本的保险,任何一步失败立刻退出,不会出现「数据库导失败但文件照样传上云」这种假成功。本地留 7 天,云端靠 rclone copy 累积,删本地不影响远端。

脚本里所有命令写绝对路径,因为 cron 的环境变量很短,PATH 里没有 /usr/local/bin,靠命令名调用 wp 或者 rclone 会莫名其妙地失败。

定时跑起来

0 4 * * * /bin/bash /root/backup.sh >> /var/log/backup.log 2>&1

宝塔用户也可以在「计划任务」里加一条 Shell 脚本,任务名写清楚,不然过阵子自己都不记得它在干什么。

怎么确认它真的生效

tail -5 /var/log/backup.log                    # 能看到 backup ok
ls -lh /root/backup/                           # 大小是不是正常量级
rclone ls oss:mybackup/你的主机名/ | head      # 云端有没有
rclone check /root/backup oss:mybackup/你的主机名/ --include "*.gz"

最后一条逐个文件比对,输出 0 differences 才叫传完整了。

恢复,顺序别搞反

先文件后数据库:

cd /www/wwwroot/littlesheep.cc
tar -xzf /root/backup/wpcontent-2026-09-22.tar.gz -C /www/wwwroot/littlesheep.cc
gunzip -c /root/backup/db-2026-09-22.sql.gz | wp db import - --allow-root
chown -R www:www wp-content

导完刷缓存,wp cache flush 走一遍,清掉 Cache Enabler 的页面缓存,接了 CDN 或者 ESA 就顺手刷一遍边缘,否则前台还是旧页面,很容易误判成恢复失败。域名也变过的话,按搬家那篇的步骤用 wp search-replace 换地址,别直接改数据库里的 URL。

几句实话

备份的价值不在生成,在能不能恢复,没被真恢复过一次的备份只能算心理安慰。我现在每季度会把最近一个包解开,导进一个临时站跑一遍,花十几分钟,比出事那天手忙脚乱翻文档划算太多。备份也不是安全的替代品,站点被挂马的时候那份包里同样带着马,清理思路可以看安全加固那篇。想让「脚本今天有没有跑」这件事主动告诉你,Uptime Kuma 盯着日志文件就够用,ESA 那篇里刷缓存的命令恢复完正好再用一次。

你的备份放在哪,多久没验过了?评论区聊聊。

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容