
背景服务器上几个服务的数据都存在 Docker Volume 里uptime-kuma → Kuma 的所有监控配置、历史记录mariadb-data → 数据库所有表和数据容器本身随时可以删了重建但Volume 里的数据丢了就真丢了。想做到定时备份 Volume 数据不停机不能为了备份把 Kuma 停了能恢复备份出来要验证真能还原原因Docker Volume 默认存在 /var/lib/docker/volumes/但你不能直接 cp原因 Docker Volume 默认存在 /var/lib/docker/volumes/但你不能直接 cp为什么不推荐容器正在写文件时拷出来的数据可能是半截的权限、属主可能丢失MariaDB 这种数据库直接拷文件大概率文件损坏正确做法用临时容器挂载 volume在容器里打包。解决一、备份临时容器打包#!/usr/bin/env bashset-uopipefailVOLuptime-kumaBACKUP_DIR/home/user/backupsDATE$(date%Y%m%d_%H%M%S)OUT$BACKUP_DIR/volume_${VOL}_${DATE}.tar.gzmkdir-p$BACKUP_DIRdockerrun--rm\-v$VOL:/data:ro\-v$BACKUP_DIR:/out\alpinetarczf/out/$(basename$OUT)-C/data.echo备份完成:$OUT关键点参数作用-v $VOL:/data:ro把 volume 挂进来只读-v $BACKUP_DIR:/out把备份目录挂进来alpine极小的镜像几 MBtar czf打包压缩为什么加 :ro只读挂载防止备份过程中意外修改数据。二、验证备份内容打包完立刻验证别等需要恢复时才发现包是空的。tar-tzf$OUT|head-20期望能看到 Kuma 的数据库文件、配置文件等。$tar-tzfvolume_uptime-kuma_20261013_030000.tar.gz|head./ ./kuma.db ./kuma.db-shm ./kuma.db-wal ./upload/ ./screenshots/如果只看到 ./ 一行说明打包时 volume 是空的挂载点写错或容器没起过。三、恢复临时容器解包假设 uptime-kuma 容器挂了要恢复数据# 1. 停掉正在跑的容器 docker stop uptime-kuma # 2. 备份当前 volume万一恢复失败还能回滚 docker run --rm \ -v uptime-kuma:/data \ -v /home/user/backups:/out \ alpine tar czf /out/volume_uptime-kuma_before_restore_$(date %s).tar.gz -C /data . # 3. 清空 volume docker run --rm \ -v uptime-kuma:/data \ alpine sh -c rm -rf /data/* /data/..?* /data/.[!.]* # 4. 解包恢复 docker run --rm \ -v uptime-kuma:/data \ -v /home/user/backups:/backup:ro \ alpine sh -c tar xzf /backup/volume_uptime-kuma_20261013_030000.tar.gz -C /data # 5. 起容器 docker start uptime-kuma四、MariaDB 的 volume 备份数据库不能用 tar 直接打包——文件可能在磁盘上不一致。正确做法# 先用 mariadb-dump 导出逻辑备份 docker exec my-mariadb mariadb-dump -uroot -p$DB_PASS test_db db.sql # 再 tar 打包把 dump 文件和 volume 一起 docker run --rm \ -v mariadb-data:/data:ro \ -v /home/user/backups:/out \ alpine sh -c tar czf /out/mariadb_volume_$(date %s).tar.gz -C /data . # 逻辑备份单独存一份 gzip db.sql mv db.sql.gz /home/user/backups/为什么两种都做备份类型优点缺点mariadb-dump逻辑可读、跨版本、可以只恢复一张表慢、占空间大tar volume物理快、省空间必须同版本、不能部分恢复两者互补都要有。验证效果模拟灾难# 1. 记录当前 Kuma 里的监控数量dockerexecuptime-kuma sqlite3/app/data/kuma.dbSELECT COUNT(*) FROM monitor;# 输出18# 2. 删掉 volume模拟灾难docker stop uptime-kuma docker rm uptime-kuma docker volume rm uptime-kuma# 3. 重新创建容器cd/home/user/uptime-kuma docker compose up-d# 4. 此时监控数量应该是 0空数据库dockerexecuptime-kuma sqlite3/app/data/kuma.dbSELECT COUNT(*) FROM monitor;# 输出0# 5. 恢复备份docker stop uptime-kuma docker run--rm -v uptime-kuma:/data -v /home/user/backups:/backup:ro \alpine sh-ctar xzf /backup/volume_uptime-kuma_20261013_030000.tar.gz -C /datadockerstartuptime-kuma# 6. 验证dockerexecuptime-kuma sqlite3/app/data/kuma.dbSELECT COUNT(*) FROM monitor;# 输出N ✅从 0 到 18完整恢复。三个坑坑 1直接 cp volume 目录# 错误sudocp-r/var/lib/docker/volumes/uptime-kuma/_data /backup/后果SQLite/MariaDB 文件可能损坏复制到一半被写属主、权限、SELinux 上下文可能丢失恢复后容器起不来正确用临时容器挂载 tar。坑 2恢复时忘了清空原 volume# 错误直接解包到已有数据的 volume docker run --rm -v uptime-kuma:/data -v /backup:/backup:ro \ alpine sh -c tar xzf /backup/xxx.tar.gz -C /data后果新数据和旧数据混在一起可能出现数据库文件冲突SQLite 报错配置合并错乱正确解包前先清空。清空命令要小心用alpine sh -c rm -rf /data/* /data/..?* /data/.[!.]*这行的含义删掉所有文件包括 . 开头的隐藏文件。坑 3备份完不验证常见备份脚本写好了跑了一年从没检查过包里有什么。出事那天$tar-tzfbackup.tar.gz tar: This does notlooklike atararchive或者$tar-tzfbackup.tar.gz ./空的。正确做法每次备份后至少验证两件事包能解压tar -tzf xxx.tar.gz /dev/null包非空tar -tzf xxx.tar.gz | wc -l 1进阶定期做恢复演练本文第四部分别等真出事。