使用 BorgBackup 備份你的 Minecraft 地圖

Minecraft 地圖是伺服器最重要的資產,但它也是最容易壞的東西之一:玩家不小心炸壞家、插件升級導致 region 檔損毀、硬碟偶發 I/O 錯誤……任何一個意外都可能讓玩家數個月的努力付諸流水。

過去我在磐石伺服器是用 git 來備份地圖,但 git 並不適合處理大量二進位的 region 檔,repo 一下就膨脹到幾百 GB。後來換成 BorgBackup,整個世界從此清爽。

為什麼是 BorgBackup?

最直接的理由:區塊級去重 (chunk-level deduplication)

Minecraft 的地圖會切成一個個 region 檔(r.x.z.mca),每個 region 大約 32×32 個 chunk。玩家活動時,往往只有幾個 chunk 真的有改動,但整個 region 檔的 mtime 會被刷新——對 rsync、tar、git 這類工具來說,整個檔案都得重傳一次。

Borg 不一樣:它會把每個檔案切成內容定址 (content-addressed) 的小區塊,只有真的改變的區塊才會被新增到 repo。對 Minecraft 來說,這個特性簡直是天作之合。

來看磐石伺服器目前的實際數字:

1
2
3
4
5
6
$ borg info /data/backup/world-borg
Original size Compressed size Deduplicated size
All archives: 2.51 TB 1.45 TB 208.68 GB

Unique chunks Total chunks
Chunk index: 255500 1783167

7 份每日快照、原始資料 2.51 TB,最後實際佔用磁碟只有 208 GB——壓縮率約 12 倍。如果換成 git 或單純 tar,這個數字大概會嚇死人。

安裝與初始化

Ubuntu 直接用 apt 就好:

1
sudo apt install borgbackup

接著建立 repo。我把 repo 放在另一顆掛載到 /data 的硬碟上,這樣即使系統碟掛掉也還救得回來:

1
borg init --encryption=none /data/backup/world-borg

如果 repo 是放在自己看得到的本機硬碟,--encryption=none 即可。如果是丟到 NAS、VPS 或共享儲存上,請改用 repokeykeyfile 模式,這樣即使 repo 外洩也不會被讀走。

最基本的備份指令

1
2
3
4
5
REPO=/data/backup/world-borg

borg create --stats --compression zstd,3 \
"$REPO::{now:%Y-%m-%d_%H%M%S}" \
~/server/world ~/server/world_nether ~/server/world_the_end

幾個重點:

  • ::{now:%Y-%m-%d_%H%M%S} 是 archive 的命名格式,Borg 會在執行時把 {now:...} 換成當下時間,每次跑就是一個新的快照。
  • --compression zstd,3 用 zstd 壓縮等級 3,速度與壓縮比的甜蜜點。對 region 檔的內部資料壓縮效果非常好。
  • 三個 world 一次傳進去,Borg 會在同一份 archive 裡記錄它們,省下一次次開關 repo 的 overhead。

第一次跑會花比較久(要建立完整的 chunk index),之後每天的增量備份在我的伺服器上只要幾分鐘。

Hot Backup:不停機的備份才是真備份

直接 borg create 看似可行,但其實有個地雷:Minecraft 隨時都在寫入 region 檔。如果你在它寫到一半時讀走,可能會拿到一個半新半舊的檔案,將來還原時就會看到莫名其妙的洞或者 region corrupt 的訊息。

正確做法是搭配 Minecraft 的 console 指令:

1
2
3
4
save-off          # 暫停自動存檔
save-all flush # 把目前記憶體的東西全部 flush 到磁碟
borg create ... # 此時磁碟上的 region 檔是一致的,可以安心備份
save-on # 恢復自動存檔

磐石伺服器這段邏輯是用 Python 包出來的,重點摘錄如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
say('地圖備份即將開始(不停機)')
borg_success = False
try:
server_util.exec_server_cmd('save-off')
server_util.exec_server_cmd('save-all flush')

# 等 log 出現 "Saved the game" 才繼續
_wait_for_save_complete(timeout=60)

server_util.exec_cmd_checked(
f'borg create --stats --compression {compression} {archive} {sources}')
borg_success = True
finally:
# 不管備份成功與否,一定要恢復自動存檔,否則伺服器會永遠不存檔
server_util.exec_server_cmd('save-on')

兩個非常重要的細節:

  1. 要等 Saved the game 真的出現在 log 才開始 borg createsave-all flush 是非同步的,馬上接 borg create 有機會撈到還沒寫完的檔案。
  2. save-on 一定要包在 finally。如果 borg 中途出錯而沒呼叫 save-on,伺服器就會永遠不存檔,下一次當機你會哭出來。

Prune:自動清掉舊備份

Borg 的 prune 可以照保留策略自動刪除舊 archive。磐石的設定是「保留最近 7 天」:

1
2
borg prune --stats --keep-daily=7 /data/backup/world-borg
borg compact /data/backup/world-borg
  • --keep-daily=7:每天只保留一份,總共保留 7 份。Borg 也支援 --keep-weekly--keep-monthly--keep-yearly,可以混搭。
  • borg compact 是 Borg 1.2 之後新增的步驟,它會把 prune 之後 repo 裡的空洞真正釋放回磁碟,否則你會發現「明明刪了卻沒變小」。

還原

備份的價值在於你真的能還原。先看一下有哪些 archive:

1
borg list /data/backup/world-borg
1
2
3
4
2026-04-08_030011  Wed, 2026-04-08 03:00:12
2026-04-09_030010 Thu, 2026-04-09 03:00:10
...
2026-04-14_025503 Tue, 2026-04-14 02:55:03

要把某個 archive 完整解出來:

1
2
mkdir /tmp/restore && cd /tmp/restore
borg extract /data/backup/world-borg::2026-04-13_025506

只想拿出某個玩家家附近的 region 檔?Borg 支援 path 過濾:

1
2
borg extract /data/backup/world-borg::2026-04-13_025506 \
home/codingman/server/world/region/r.0.0.mca

如果只是想瀏覽備份內容而不解開,可以用 borg mount 把 repo 掛成 FUSE 檔案系統,超方便。

異地備份:別把雞蛋放在同一個籃子

本機 borg repo 再可靠,硬碟壞掉還是一切歸零。磐石的做法是在備份完成後,順手把整個 repo 用 gsutil rsync 到 Google Cloud Storage:

1
2
gsutil -m rsync -r -i -j -d \
/data/backup/world-borg gs://rock-mc-usa

Borg repo 的好處是它本身已經是去重壓縮過的檔案集合,rsync 上去的流量也會跟著省。對於只想要「萬一機房整個炸了還能重建世界」的需求來說,這個組合相當划算。

結語

從 git 換到 BorgBackup 之後,磐石伺服器再也不用擔心 repo 膨脹、push 卡住,每天自動跑、自動 prune、自動上雲,凌晨三點完成,玩家完全感覺不到。如果你也在維護 Minecraft 伺服器(或任何「大量檔案、小幅修改」的工作負載),非常推薦試試看 Borg。

最後再強調一次最重要的 checklist:

  • save-offsave-all flush → 等 log → borg createsave-on,順序不能亂。
  • save-on 必須在 finally 裡。
  • prune 之後記得 compact,不然空間不會真的釋放。
  • repo 要做異地備份,本機 repo 不算「備份」,只算「方便還原的副本」。

也許你也會想看看