ความพร้อมในการกู้คืน
Libre WebUI มีรายการสำรวจการกู้คืนแบบอ่านอย่างเดียวเป็นด่านความปลอดภัยแรกสำหรับการสำรองและกู้คืน รายงานจะระบุสถานะที่ทราบว่ามีอยู่และเงื่อนไขที่ตรวจพบซึ่งขัดขวางการสร้าง snapshot แต่จะไม่รับ maintenance lock, คัดลอก, เข้ารหัส, อัปโหลด, ลบ, ซ่อม หรือกู้คืนข้อมูล
libre-webui recovery-check --json > recovery-inventory.json
เมื่อใช้ source checkout ให้รัน npm run build:backend หนึ่งครั้ง แล้วแทน libre-webui recovery-check ด้วย npm run recovery:check -- การติดตั้งแบบ npx และ Homebrew จะตรวจ ~/.libre-webui ตามค่าเริ่มต้น โดย DATA_DIR และตัวเลือกเส้นทางที่ระบุชัดเจนจะแทนตำแหน่งนั้น
คำสั่งจะออกด้วยสถานะ 0 เมื่อไม่พบสิ่งกีดขวาง, 1 เมื่อรายงานสมบูรณ์แต่มีสิ่งกีดขวางการกู้คืน และ 2 เมื่ออาร์กิวเมนต์ไม่ถูกต้องหรือเกิดความล้มเหลวที่ไม่คาดคิดขณะรวบรวม ใช้ --data-dir PATH หรือ --database PATH เพื่อตรวจตำแหน่งที่ไม่ใช่ค่าเริ่มต้น รายการ volume แบบค่าเริ่มต้นหรือ --data-dir ยอมรับเฉพาะไฟล์มาตรฐาน DATA_DIR/data.sqlite และปฏิเสธรายการฐานข้อมูล/WAL/SHM ที่เป็น hard link, symlink หรือไม่ใช่ไฟล์ปกติ เส้นทาง --database ที่ระบุชัดเจนอาจอยู่นอก DATA_DIR แต่ฐานข้อมูลที่เลือกและไฟล์ประกอบยังต้องเป็นไฟล์ปกติและห้ามเป็น symlink เมื่อใช้ --database โดยไม่มี --data-dir ระบบกู้คืนจะถือโฟลเดอร์แม่ของฐานข้อมูลเป็นรากข้อมูล เพื่อสำรวจคีย์, blob และคำนิยามปลั๊กอินที่ตรงกันร่วมกัน
runtime ยังอ่านคำนิยามปลั๊กอินย้อนหลังจากไดเรกทอรี plugins ในแพ็กเกจ backend ที่กำหนดแน่นอน และสำหรับ PLUGINS_DIR แบบสัมพัทธ์ จะอ่านตำแหน่งย้อนหลังที่สัมพันธ์กับ backend ด้วย การกู้คืนจะสำรวจเส้นทาง legacy ที่ใช้งานเหล่านั้น และป้องกัน snapshot เฉพาะ volume เมื่อมีคำนิยามแบบกำหนดเอง การติดตั้งแบบแพ็กเกจส่ง --legacy-plugins-dir PATH ได้มากกว่าหนึ่งครั้ง หากโครงสร้าง image ย้ายไดเรกทอรีเพื่อความเข้ากันได้เหล่านั้น
สำหรับการติดตั้ง Compose แบบส่วนตัว ให้รันภายในคอนเทนเนอร์ที่ติดตั้ง เพื่อให้รายงานอธิบาย volume, โค้ด และ secret ที่ mount ของคอนเทนเนอร์นั้น:
docker exec libre-webui \
libre-webui recovery-check --json --data-dir /app/backend/data
สิ่งที่รายการสำรวจตรวจสอบ
รายงาน JSON ที่มีเวอร์ชันจะบันทึก:
- เวอร์ชันแอป, Node.js, ระบบปฏิบัติการ และสถาปัตยกรรม;
- ขนาดไฟล์ SQLite และ WAL/SHM,
quick_check, การตรวจ foreign key, ลายนิ้วมือ schema, เวอร์ชันผู้ใช้, ตารางจำเป็นที่ขาด และการตรวจไฟล์ต้นทางแบบไม่ตามลิงก์ก่อนสร้าง snapshot ส่วนตัวสำหรับตรวจ; - ความสามารถในการอ่านและเขียนไดเรกทอรีข้อมูล จำนวนไฟล์ และจำนวนไบต์;
- แหล่งคีย์เข้ารหัสที่เลือกและลายนิ้วมือทางเดียว 16 อักขระ;
- การตรวจแบบไม่ตามลิงก์และลิงก์เดียวสำหรับไฟล์
.encryption_keyถาวร; - การมีอยู่ จำนวน ขนาด และการรวมไว้ในไดเรกทอรีข้อมูลของคำนิยามปลั๊กอินแบบกำหนดเอง รวมถึงราก blob ในเครื่องที่เข้ารหัส, media ที่ฝัง, ข้อมูลอ้างอิงเสียง, ข้อความเอกสาร, เวกเตอร์เอกสาร legacy และเวกเตอร์แพลตฟอร์มพร้อมแถว ACL/ตัวกรอง;
- การยืนยันตัวตนแบบอ่านอย่างเดียวและมีขอบเขตของออบเจ็กต์ blob ในเครื่องมาตรฐานทุกชิ้นและ envelope เวกเตอร์แพลตฟอร์มที่ฝัง รวมถึงการตรวจ chunk/checksum ของ blob ทั้งหมดและคีย์ที่กำหนดไว้;
- การยืนยันตัวตนแบบอ่านอย่างเดียวและมีขอบเขตของ envelope ข้อความ AES-GCM แบบ legacy ที่รู้จักได้ทั้งหมดในแชต โน้ต เอกสาร การตั้งค่า secret ปลั๊กอิน สถานะแกลเลอรี/media และอีเมลบัญชี รวมถึงชื่อเสียงที่บันทึก การบันทึก และข้อความถอดเสียงทุก envelope ซึ่งผูก AAD;
- จำนวน task/run/preview ของ Work และ Docker volume, Kubernetes PVC หรือข้อมูลระบุเส้นทางโฮสต์แบบแฮชที่คาดไว้ โดย Docker volume ต้องมีทั้ง managed label และ task ID เจ้าของที่ตรงทุกตัว;
- สถานะงานสร้าง media แบบ legacy รวมถึง durable job แยกตามสถานะ, attempt แยกตามผล, จำนวน event stream/event และ cursor เหตุการณ์ส่วนกลางล่าสุด;
- การยืนยันตัวตนแบบอ่านอย่างเดียวและมีขอบเขตของ payload durable job และ event ที่เข้ารหัสทุกชิ้น พร้อมการตรวจไวยากรณ์แบบมีขอบเขตของ payload อ้างอิงแบบ opaque ทุกชิ้น; และ
- สิ่งกีดขวาง คำเตือน และข้อมูลที่อยู่นอกไดเรกทอรีข้อมูลแอปอย่างชัดเจน
รายงานจะไม่รวมคีย์เข้ารหัส, JWT/session secret, ข้อมูลรับรองผู้ให้บริการ, เนื้อหาปลั๊กอิน, เนื้อหาผู้ใช้ หรือเส้นทางพื้นที่ทำงานบนโฮสต์แบบข้อความจริง โดยส่งออกเพียงค่า boolean ว่ามี secret และลายนิ้วมือคีย์เข้ารหัสที่ย้อนกลับไม่ได้
data mount แบบอ่านอย่างเดียวใช้ตรวจการกู้คืนได้และจะให้คำเตือน ไม่ใช่สิ่งกีดขวาง แต่ความพร้อมของแอปยังต้องใช้พื้นที่เขียนได้ ห้ามเริ่ม Libre WebUI กับ snapshot แบบอ่านอย่างเดียวที่เครื่องมือสำรองใช้
สิ่งกีดขวาง
ให้ถือว่าสิ่งกีดขวางทุกอย่างทำให้ด่านการกู้คืนล้มเหลว ตัวอย่างทั่วไป ได้แก่ ฐานข้อมูลหายหรือเสียหาย, schema ไม่สมบูรณ์, คีย์หายหรือขัดแย้ง, ciphertext แบบ legacy หรือแพลตฟอร์มเสียหาย/ยืนยันตัวตนไม่ได้, เกินขอบเขตการตรวจ, ไดเรกทอรีข้อมูลอ่านไม่ได้, ต้นทาง SQLite เป็นลิงก์หรือไม่ใช่ไฟล์ปกติ, Work run หรือ preview ที่กำลังทำงาน, งาน media หรือ durable job, พื้นที่ทำงาน Work หายหรือติด label ผิด, หัว event แบบ durable ไม่ตรงหรือมีช่องว่างลำดับ, คำนิยามปลั๊กอินแบบกำหนดเองอยู่นอกไดเรกทอรีข้อมูล หรือ control plane ของ runtime ตรวจพื้นที่ทำงานภายนอกไม่ได้ ให้หยุดงานที่กำลังทำและแก้ dependency ที่ขาดก่อนสร้าง snapshot ห้ามแก้รายงานเพื่อซ่อนสิ่งกีดขวาง
payload durable ที่เข้ารหัสจะยืนยันตัวตนเทียบกับตัวตน job/event และตรวจเป็น JSON มาตรฐานที่มีขอบเขต ส่วน payload อ้างอิงแบบ opaque จะตรวจเพียงขนาดและไวยากรณ์ เพราะระบบปัจจุบันไม่มี repository อ้างอิง blob ที่เป็นแหล่งตัดสินเพื่อพิสูจน์ว่าเป้าหมายมีอยู่หรือเข้าถึงได้ รายงานจะกำหนด referenceTargetsVerified เป็น false และเตือนเมื่อมีการอ้างอิง โดยไม่เปิดเผยค่า payload หรือ reference
ฟิลด์ข้อความ legacy เกิดก่อนมีเครื่องหมาย envelope บังคับ ดังนั้นแถว plaintext จริงจาก schema รุ่นเก่ายังคงอ่านได้และจะไม่ถูกรายงานเป็น ciphertext ที่ยืนยันตัวตนแล้ว envelope มาตรฐานจะถูกยืนยันเสมอ ค่าแบบสามส่วนที่มี IV หรือ authentication tag ขนาดเท่า envelope จะปฏิเสธอย่างปลอดภัยเมื่อผิดรูปแบบ ฟิลด์เสียงที่บันทึกมี binary envelope ชัดเจนและต้องยืนยันกับโปรไฟล์ เจ้าของ และตัวตนฟิลด์เสมอ ส่วน JSON encryption.legacyCiphertext รายงานจำนวน record และ byte ของข้อความ/ไบนารีที่ยืนยันแล้วโดยไม่เปิดเผย plaintext
เมื่อ schema v4 มีคอลัมน์ users.email_lookup การกู้คืนจะยืนยันอีเมลที่ไม่ใช่ null ทุกค่าและคำนวณ lookup token แบบใช้คีย์ที่แยก domain ใหม่ token ที่หาย ไม่ตรง หรือผูกกับอีเมล null จะขัดขวาง snapshot ฐานข้อมูลก่อน v4 ยังเข้ากันได้เพราะไม่มีคอลัมน์ lookup ที่คำนวณนี้
ขอบเขตการสำรองปัจจุบัน
เครื่องมือช่วยของการติดตั้งส่วนตัวจะหยุดแอปหากกำลังทำงาน แล้วใช้ image ที่เปลี่ยนไม่ได้, data volume ที่ mount และสภาพแวดล้อมของคอนเทนเนอร์นั้นเพื่อสร้าง archive แบบเดี่ยวที่รวมทุกส่วน manifest ลงลายเซ็น Ed25519 และ payload ทั้งหมดเข้ารหัสด้วยคีย์สำรอง AES-256-GCM ที่ผู้ดำเนินการถือ ประกอบด้วย SQLite, blob ในเครื่องและเวกเตอร์ที่ฝัง, ตัวเลือก runtime และการกำหนดค่าที่ป้องกันไว้ซึ่งจำเป็นต่อการถอดรหัสสถานะที่กู้คืน เครื่องมือจะตรวจลายเซ็น checksum ของ ciphertext และ payload ที่ถอดรหัสก่อนเผยแพร่ archive กับรายงาน metadata libre-webui-restore ยอมรับเฉพาะ Docker volume ใหม่ ตรวจรายการกู้คืนที่ถอดรหัสแล้วก่อนคัดลอกข้อมูล และเผยแพร่การกำหนดค่าที่กู้คืนเป็นไฟล์ส่วนตัวในไดเรกทอรีเป้าหมายใหม่
การกำหนดค่า runtime ที่ป้องกันไว้รวม timeout ของ pool, connection, idle, statement และ migration lock สำหรับ PostgreSQL; timeout การเชื่อมต่อ Redis; การตั้งค่าโควตา blob ถาวรทั้งสอง; ตัวเลือกแพลตฟอร์ม; prefix S3 และโหมด addressing ค่าเหล่านี้อยู่ใน payload ที่ลงลายเซ็นและเข้ารหัส ไม่ใช่ manifest แบบ plaintext และจะเผยแพร่ใหม่เป็นการกำหนดค่าโหมด 0600 เมื่อใช้การกู้คืน
archive แบบเดี่ยวไม่รวม Docker volume ของ Work, Kubernetes PVC, โฟลเดอร์พื้นที่ทำงานที่ผูกกับโฮสต์, โมเดล Ollama หรือสถานะผู้ให้บริการภายนอก ให้แสดงรายการยกเว้นใน signed manifest เหล่านี้ไว้และ snapshot พื้นที่เก็บ Work ภายนอกแยกต่างหาก โปรไฟล์ทีมใช้กระบวนการออฟไลน์แยก: snapshot ส่งออก PostgreSQL, ออบเจ็กต์ ciphertext S3 ที่มีเวอร์ชันตรงทุกตัว, รายการ PGVector, การกำหนดค่า runtime และตัวตนคีย์ จะถูก seal ลงใน archive แบบลงลายเซ็น/เข้ารหัสเดียวกัน และตรวจเทียบกับเป้าหมาย PostgreSQL/S3 ที่สะอาดระหว่างกู้คืน cache, presence, wake-up และ lease ของ Redis จะสร้างใหม่จากสถานะ SQL มาตรฐาน
การสำรองทีมยังยืนยัน payload durable job และ event ที่เข้ารหัสและมีขอบเขตทุกชิ้นภายใน snapshot PostgreSQL ที่ส่งออก รายการที่ป้องกันและลงลายเซ็นจะบันทึกยอดรวม job, event, stream, cursor, envelope, reference และ plaintext ที่ยืนยันแล้ว ทุก event stream ต้องมีลำดับต่อเนื่อง 1..last_sequence อย่างแม่นยำ และลำดับ cursor ส่วนกลางของ PostgreSQL ต้องไม่ล้าหลัง cursor สูงสุดที่เก็บ การกู้คืนจะตรวจซ้ำกับเป้าหมายสะอาดและกำหนดให้ผลทั้งหมดตรงกับรายการต้นทางที่ลงลายเซ็นก่อนรายงานความสำเร็จ ช่องว่างระหว่างค่า cursor ส่วนกลางที่ต่างกันใช้ได้ เพราะการจัดสรร identity ของ PostgreSQL ไม่เป็น transactional; ลำดับต่อ stream คือสัญญาการเรียงต่อเนื่อง
เมื่อ PLUGINS_DIR ชี้ออกนอก DATA_DIR การกู้คืนจะสำรวจไดเรกทอรีนั้นและทำเครื่องหมายว่าไม่รวมใน archive ของ application volume คำนิยามใด ๆ ที่นั่นจะขัดขวาง snapshot เฉพาะ volume จนผู้ดำเนินการจัด snapshot ไดเรกทอรีปลั๊กอินที่ตรงกัน กฎเดียวกันใช้กับไดเรกทอรีปลั๊กอิน legacy ที่ใช้งาน คำนิยาม JSON ที่เป็น symlink, ไม่ใช่ไฟล์ปกติ หรืออ่านไม่ได้เป็นสิ่งกีดขวางเสมอ และจะไม่ถูกตามหรือข้ามอย่างเงียบ ๆ
durable job และ event ที่เรียงลำดับทำงานในทั้งสองโปรไฟล์ การกู้คืนจะขัดขวางขณะที่ job attempt หรือการทำงาน Work กำลังทำงาน ตรวจ payload job/event และหัว stream ที่ต่อเนื่อง และรักษาสถานะ SQL มาตรฐาน โปรไฟล์เดี่ยวรัน embedded worker ที่มีขอบเขต ส่วนทีมรัน handler ที่ลงทะเบียนเดียวกันใน worker ภายนอกและใช้ Redis เฉพาะการปลุกกับ fan-out
สำหรับระบบใช้งานจริง ให้เก็บคีย์เข้ารหัสและ JWT secret ในตัวจัดการ secret ที่ป้องกันไว้ เก็บ backup archive แบบเข้ารหัสไว้นอกโฮสต์ และทดสอบการกู้คืนในสภาพแวดล้อมที่สะอาดและเข้ากันได้ รายการสำรวจเป็น snapshot ก่อนดำเนินการของสถานะที่ทราบ ไม่ใช่ maintenance lock หรือหลักฐานอิสระว่าทรัพยากรภายนอกทั้งหมดกู้คืนได้
คำสั่งสำรองแบบลงลายเซ็นและเข้ารหัส
ตัวอย่างด้านล่างใช้คำสั่ง libre-webui ที่ติดตั้งจาก npm แบบ global หรือ Homebrew หากไม่ติดตั้งแบบ global ให้แทน libre-webui ด้วย npx --yes libre-webui@latest จาก source checkout ให้ build backend หนึ่งครั้งและแทน libre-webui backup ด้วย npm run recovery:backup -- image Docker สำหรับระบบใช้งานจริงมีคำสั่งเดียวกันที่ /usr/local/bin/libre-webui การสำรองและกู้คืนทีมต้องใช้ pg_dump และ pg_restore ของ PostgreSQL 16 เพิ่มเติม ซึ่งรวมอยู่ใน image ระบบใช้งานจริงและเส้นทางคำสั่งของสูตร Homebrew ให้ติดตั้งไคลเอ็นต์ PostgreSQL ที่เข้ากันได้ก่อนใช้คำสั่งเหล่านี้จาก npm/npx ปกติ
สร้างคีย์ archive AES-256-GCM และคู่คีย์ลงลายเซ็น Ed25519 ที่ผู้ดำเนินการถือในไดเรกทอรีส่วนตัว แล้วนำ private key ไปเก็บนอกโฮสต์อย่างปลอดภัย:
install -d -m 0700 /absolute/private/libre-backup-keys
libre-webui backup keygen \
--directory /absolute/private/libre-backup-keys
สำหรับไดเรกทอรีข้อมูลเดี่ยวที่หยุดนิ่ง ให้สร้างและตรวจ archive แยกต่างหาก:
libre-webui backup create \
--offline \
--data-dir /absolute/path/to/libre-data \
--output /absolute/backups/libre-solo.lwbackup \
--encryption-key /absolute/private/libre-backup-keys/backup-encryption.key \
--signing-private-key /absolute/private/libre-backup-keys/backup-signing-private.pem
libre-webui backup verify \
--archive /absolute/backups/libre-solo.lwbackup \
--encryption-key /absolute/private/libre-backup-keys/backup-encryption.key \
--signing-public-key /absolute/private/libre-backup-keys/backup-signing-public.pem
ทดสอบก่อนกู้คืน แล้วใช้จริงเฉพาะกับไดเรกทอรีเป้าหมายใหม่ที่ว่าง:
libre-webui backup restore-preflight \
--archive /absolute/backups/libre-solo.lwbackup \
--target /absolute/restore/libre-data \
--encryption-key /absolute/private/libre-backup-keys/backup-encryption.key \
--signing-public-key /absolute/private/libre-backup-keys/backup-signing-public.pem
libre-webui backup restore-apply \
--archive /absolute/backups/libre-solo.lwbackup \
--target /absolute/restore/libre-data \
--encryption-key /absolute/private/libre-backup-keys/backup-encryption.key \
--signing-public-key /absolute/private/libre-backup-keys/backup-signing-public.pem
libre-webui backup restore-verify \
--target /absolute/restore/libre-data
สำหรับโหมดทีม ให้หยุด replica และ worker ของแอปทั้งหมด คงสภาพแวดล้อม PostgreSQL/S3/keyring ต้นทางไว้ แล้วสร้าง archive ที่ประสานกัน:
libre-webui backup create-team \
--offline \
--output /absolute/backups/libre-team.lwbackup \
--encryption-key /absolute/private/libre-backup-keys/backup-encryption.key \
--signing-private-key /absolute/private/libre-backup-keys/backup-signing-private.pem
โหลดตัวแปรสภาพแวดล้อมสำหรับฐานข้อมูล PostgreSQL ใหม่ที่ว่างและ bucket S3 แบบมีเวอร์ชันที่ว่างก่อนกู้คืน ขั้น preflight จะตรวจลายเซ็นและ archive ที่เข้ารหัส ตรวจรายการที่ป้องกัน และพิสูจน์ว่าฐานข้อมูลเป้าหมายกับ prefix ของ bucket ว่างโดยไม่เผยแพร่ข้อมูล ขั้น apply จะกู้คืนไปยังเป้าหมายสะอาด ตรวจ schema PostgreSQL, ออบเจ็กต์ S3 ที่ตรงทุกตัว และระเบียน PGVector แล้วเขียนการกำหนดค่า runtime ที่ป้องกันไว้ในไดเรกทอรีส่วนตัวใหม่:
libre-webui backup restore-team-preflight \
--archive /absolute/backups/libre-team.lwbackup \
--encryption-key /absolute/private/libre-backup-keys/backup-encryption.key \
--signing-public-key /absolute/private/libre-backup-keys/backup-signing-public.pem
libre-webui backup restore-team-apply \
--archive /absolute/backups/libre-team.lwbackup \
--configuration-output /absolute/restore/libre-team-config \
--encryption-key /absolute/private/libre-backup-keys/backup-encryption.key \
--signing-public-key /absolute/private/libre-backup-keys/backup-signing-public.pem
ห้ามชี้การกู้คืนไปยังฐานข้อมูลต้นทาง, bucket ต้นทาง, ไดเรกทอรีข้อมูลที่มีอยู่ หรือไดเรกทอรีการกำหนดค่าที่มีไฟล์ เก็บ public signing key ไว้กับคู่มือการกู้คืน การมีเพียง archive และ public key ไม่เพียงพอสำหรับถอดรหัส payload
หากการกู้คืนทีมรายงานว่าย้อนกลับไม่สมบูรณ์ ให้ถือว่าเป้าหมายทั้งสองสกปรกและอย่าลองซ้ำทันที ตรวจและทำความสะอาดฐานข้อมูล PostgreSQL เป้าหมาย แล้วไล่รายการและลบทุก object version กับ delete marker ใต้ prefix S3 เป้าหมายที่ระบุ รัน restore-team-preflight อีกครั้ง การ apply จะลองซ้ำได้อย่างปลอดภัยหลัง preflight เป้าหมายสะอาดสำเร็จเท่านั้น
การซ้อมกู้คืนที่ตรวจสอบแล้วตามกำหนดเวลา
การสำรองที่ไม่เคยกู้คืนเป็นเพียงความหวัง ไม่ใช่การกู้คืน การซ้อมจะพิสูจน์ว่าระบบกู้คืนได้จริงด้วยการรัน pipeline ข้างต้นครบตั้งแต่ต้นจนจบ โดยไม่หยุดให้บริการและไม่ต้องมีผู้ดำเนินการ:
- จัดเตรียม snapshot ของไดเรกทอรีข้อมูลขณะหยุดนิ่ง โดยคัดฐานข้อมูล SQLite ผ่าน online backup API และคัดลอก blob กับไฟล์จริง การซ้อมจะรอช่วงเงียบและปฏิเสธการทำงานเมื่อ durable job ใดกำลังทำงาน ซึ่งเป็นกฎเดียวกับ
recovery-check - สำเนาที่จัดเตรียมจะกลายเป็น archive ที่ลงลายเซ็นและเข้ารหัส AES-256-GCM ด้วยคีย์ชั่วคราวสำหรับการซ้อม พร้อมรันรายการสำรวจการกู้คืนทั้งหมด
- ระบบตรวจ archive, กู้คืนไปยังเป้าหมายชั่วคราวที่แยกออกจากกัน และตรวจสภาพแวดล้อมที่กู้คืนอีกครั้ง
- การซ้อมบันทึกสิ่งที่วัดได้ ระยะเวลากู้คืนคือ RTO ที่พิสูจน์แล้ว และช่วงห่างระหว่างการซ้อมที่สำเร็จกำหนดขอบเขต RPO ที่ทำได้ของกำหนดเวลาปัจจุบัน จากนั้นลบ artifact ทั้งหมด การซ้อมเป็นการตรวจ ไม่ใช่การสำรอง จึงไม่เก็บ archive หรือคีย์
เปิดกำหนดเวลาด้วย RECOVERY_DRILL_INTERVAL_HOURS (เช่น 24) จากนั้นการซ้อมจะรันบนตัวจัดกำหนดการร่วมภายใต้ coordinator lease ทำให้ replica และ tick ที่ทับกันไม่รันซ้ำ หน้า System แสดงประวัติการซ้อมพร้อมปุ่ม “Run drill now” สำหรับผู้ดูแลระบบ โดยใช้ GET /api/recovery/drills และ POST /api/recovery/drills/run การซ้อมที่ล้มเหลวโดยไม่มีผู้ดูแลจะแจ้งผู้ดูแลระบบทุกคนผ่านกล่องข้อความแจ้งเตือน (และ webhook ที่สมัครไว้) ส่วนการรันด้วยตนเองจะรายงานเหตุผลที่ปฏิเสธโดยตรง RECOVERY_DRILL_HISTORY จำกัดประวัติที่เก็บไว้ (ค่าเริ่มต้น 60 รายการ)
การซ้อมครอบคลุมโปรไฟล์เดี่ยว (SQLite) ซึ่ง archive ระบบไฟล์เป็นเส้นทางสำรองที่เป็นแหล่งตัดสิน โปรไฟล์ทีมยังใช้กระบวนการ backup create-team ที่ประสานกัน โดยขณะนี้การซ้อมกู้คืนยังเป็นขั้นตอนในคู่มือของผู้ดำเนินการ