S174-INV-04 — Code Backup to GDrive Prep
S174-INV-04 — Code Backup to GDrive Prep
Date: 2026-04-08 Scope: Investigation only, strict read-only. No cron created, no script edited, no upload test, no tarball created.
Rules Loaded
.claude/skills/incomex-rules.mdsearch_knowledge("operating rules SSOT")search_knowledge("hiến pháp v4.0 constitution")search_knowledge("backup-to-gdrive.sh pg backup kuma heartbeat")search_knowledge("s174 prep nuxt report 2 repo web-test agent-data-test")search_knowledge("operating rules v7.55 out of scope")
3 Câu Tuyên Ngôn
- Vĩnh viễn: nếu làm Bước 2, nên tái sử dụng transport đã chứng minh được (
rclone+ GDrive + Kuma), nhưng tách riêng script, folder remote, retention, monitor và log cho code backup. Không piggyback mù vàobackup-to-gdrive.sh. - Nhầm được không: phải chặn nhầm bằng folder riêng theo repo, filename có repo + timestamp ICT, verify size sau upload, lock chống chạy chồng, và monitor tách riêng từng repo.
- 100% tự động: cron chạy 4 lần/ngày, script tự đóng gói, upload, verify remote metadata, cleanup retention, push heartbeat. Không cần thao tác tay sau khi triển khai xong.
1. Executive Summary
Verdict
Khả thi. Có thể dùng cùng kênh GDrive hiện có làm secondary backup cho code của:
/opt/incomex/docker/nuxt-repo/opt/incomex/docker/agent-data-repo
Nhưng chỉ nên tái sử dụng các phần đã chứng minh ổn định:
rcloneremote hiện có- credential hiện có
- pattern log/summary
- pattern Kuma heartbeat
Không nên copy nguyên trạng các điểm sau:
- remote đang ghi thẳng vào Drive root, chưa có folder riêng cho backup code
- cron hiện tại đang chạy theo timezone host
Europe/Berlin, không theo giờ VN - log của data-backup đang bị double-write do script tự
teevào log và cron cũng redirect cùng file - script hiện tại không có lock chống chạy chồng
- heartbeat hiện tại là success-only; failure được phát hiện gián tiếp qua missed heartbeat
Kết luận ngắn
- Kênh upload GDrive hiện tại đủ tin cậy để tái sử dụng cho code backup.
- GDrive còn dư rất lớn:
Free: 4.986 TiB. - Dung lượng 2 repo nhỏ so với quota hiện có.
- Backup
4 lần/ngàyhợp lý hơnmỗi lần git commit. - Nên dùng
2 monitor Kuma riêng, không dùng 1 monitor chung.
2. Hiện Trạng Cơ Chế Data-Backup GDrive Hiện Có
2.1 Runtime timezone và cron thực tế
Đã xác minh VPS không chạy UTC; cron đang bám timezone host Europe/Berlin (CEST, +0200).
Evidence:
$ ssh root@38.242.240.89 'date -Is; echo "---"; timedatectl 2>/dev/null | sed -n "1,12p"'
2026-04-08T15:31:50+02:00
---
Local time: Wed 2026-04-08 15:31:51 CEST
Universal time: Wed 2026-04-08 13:31:51 UTC
Time zone: Europe/Berlin (CEST, +0200)
System clock synchronized: yes
Cron hiện tại gọi data-backup lúc 20:00 theo giờ host:
$ ssh root@38.242.240.89 'crontab -l 2>/dev/null | nl -ba | sed -n "52,54p"'
52 # VPS Full Backup to Google Drive (Mon+Thu 3AM Hanoi = Sun+Wed 20:00 UTC)
53 # VPS Full Backup to Google Drive (nightly 3AM Hanoi = 20:00 UTC)
54 0 20 * * * /opt/incomex/scripts/backup-to-gdrive.sh >> /opt/incomex/logs/backup-gdrive.log 2>&1
Observed reality: comment nói 20:00 UTC, nhưng host thực tế là CEST, nên line 0 20 * * * đang chạy lúc 20:00 CEST, tức khoảng 01:00 ICT, không phải 03:00 ICT.
2.2 Script hiện tại dùng gì
backup-to-gdrive.sh đang dùng rclone, remote gdrive-backup:, keep 14 remote backups và 1 local backup.
Evidence:
$ ssh root@38.242.240.89 'nl -ba /opt/incomex/scripts/backup-to-gdrive.sh 2>/dev/null | sed -n "11,24p"'
11 # --- Config ---
12 BACKUP_DIR="/opt/incomex/backups"
13 TIMESTAMP=$(date +%Y%m%d_%H%M%S)
14 BACKUP_NAME="vps-backup-${TIMESTAMP}"
17 LOG_FILE="/opt/incomex/logs/backup-gdrive.log"
18 RCLONE_REMOTE="gdrive-backup:"
19 MAX_REMOTE_BACKUPS=14
21 # --- Logging (must be BEFORE any commands that might emit errors) ---
22 mkdir -p "$(dirname "$LOG_FILE")"
23 exec > >(tee -a "$LOG_FILE") 2>&1
Upload + retention + heartbeat pattern:
$ ssh root@38.242.240.89 'nl -ba /opt/incomex/scripts/backup-to-gdrive.sh 2>/dev/null | sed -n "104,140p"'
104 # --- Upload to Google Drive ---
105 echo "Uploading to Google Drive..."
106 rclone copy "$ARCHIVE" "$RCLONE_REMOTE" \
111 # --- Cleanup local (keep only latest) ---
115 # --- Cleanup remote (keep MAX_REMOTE_BACKUPS) ---
117 REMOTE_FILES=$(rclone lsf "$RCLONE_REMOTE" --files-only | grep "^vps-backup-" | sort -r)
127 # --- Summary ---
132 REMOTE_COUNT=$(rclone lsf "$RCLONE_REMOTE" --files-only | grep -c "^vps-backup-" || echo 0)
136 # --- S174-FIX-01: Heartbeat → Uptime Kuma (pg-backup-gdrive) ---
137 KUMA_PUSH_URL="http://localhost:3001/api/push/pg-backup-gdrive"
139 curl -fsS "${KUMA_PUSH_URL}?status=up&msg=OK+archive%3D${ARCHIVE_SIZE}&ping=${ARCHIVE_BYTES}" >&2
140 echo "HEARTBEAT sent to Kuma (pg-backup-gdrive)"
2.3 Remote GDrive, quota, folder đích, credential path
Evidence:
$ ssh root@38.242.240.89 'command -v rclone 2>/dev/null; echo "---"; rclone listremotes 2>/dev/null; echo "---"; rclone about gdrive-backup: 2>/dev/null'
/usr/bin/rclone
---
gdrive-backup:
---
Total: 5 TiB
Used: 7.862 GiB
Free: 4.986 TiB
Trashed: 0 B
Other: 6.129 GiB
Remote đang ghi vào Drive root, chưa có folder backup riêng:
$ ssh root@38.242.240.89 'rclone lsf gdrive-backup: --files-only 2>/dev/null | sed -n "1,30p"'
Bảng tính không có tiêu đề.xlsx
Ghi chú lưu ý.docx
vps-backup-20260403_103342.tar.gz
vps-backup-20260403_104307.tar.gz
vps-backup-20260403_200001.tar.gz
vps-backup-20260404_200001.tar.gz
vps-backup-20260405_200002.tar.gz
vps-backup-20260406_200001.tar.gz
vps-backup-20260407_200002.tar.gz
vps-backup-20260408_122032.tar.gz
vps-backup-20260408_122159.tar.gz
Credential path và quyền truy cập:
$ ssh root@38.242.240.89 'rclone config file 2>/dev/null; echo "---"; stat -c "%n %U:%G %a %s bytes %y" /root/.config/rclone/rclone.conf 2>/dev/null; echo "---"; ls -ld /root /root/.config /root/.config/rclone 2>/dev/null'
Configuration file is stored at:
/root/.config/rclone/rclone.conf
---
/root/.config/rclone/rclone.conf root:root 644 585 bytes 2026-04-08 12:20:49.988266943 +0200
---
drwx------ 9 root root 4096 Apr 8 12:21 /root
drwxr-xr-x 5 root root 4096 Feb 28 02:19 /root/.config
drwxr-xr-x 2 root root 4096 Apr 8 15:32 /root/.config/rclone
Kết luận đã xác minh:
- remote là
gdrive-backup: - storage còn rất dư
- data-backup hiện đang đẩy vào Drive root
- credential file ở
/root/.config/rclone/rclone.conf - file config là
644, nhưng/rootlà700, nên không phơi lộ cho user không có quyền traverse/root
3. Đánh Giá Độ Tin Cậy Cơ Chế Hiện Có
3.1 Điều đã xác minh
7 ngày gần nhất không thấy WARN/ERROR/timeout trong log backup chính; các lần chạy gần đây đều hoàn tất upload và gửi heartbeat.
Evidence:
$ ssh root@38.242.240.89 'grep -nE "WARN|ERROR|Failed|failed|fatal|FATAL|curl:|denied|timeout" /opt/incomex/logs/backup-gdrive.log 2>/dev/null | tail -n 120'
(no output)
Latest successful run [excerpt]:
$ ssh root@38.242.240.89 'tail -n 140 /opt/incomex/logs/backup-gdrive.log 2>/dev/null'
...
2026/04/08 12:22:40 INFO : vps-backup-20260408_122159.tar.gz: Copied (new)
...
→ Upload complete
Cleaning local...
Cleaning remote (keep 14)...
==========================================
BACKUP DONE: 2026-04-08 12:22:41 CEST
Archive: 73M
PG: 36M | Qdrant: 63M
Remote backups: 9
==========================================
{"ok":true}HEARTBEAT sent to Kuma (pg-backup-gdrive)
Remote metadata khớp với log:
$ ssh root@38.242.240.89 'rclone lsl gdrive-backup: 2>/dev/null | sed -n "1,10p"'
75913453 2026-04-08 12:22:17.845000000 vps-backup-20260408_122159.tar.gz
75932107 2026-04-08 12:20:48.989000000 vps-backup-20260408_122032.tar.gz
76667116 2026-04-07 20:00:39.563000000 vps-backup-20260407_200002.tar.gz
Local retention cũng đúng với script hiện tại:
$ ssh root@38.242.240.89 'ls -lh /opt/incomex/backups/vps-backup-*.tar.gz 2>/dev/null'
-rw-r--r-- 1 root root 73M Apr 8 12:22 /opt/incomex/backups/vps-backup-20260408_122159.tar.gz
3.2 Reliability assessment
Đánh giá: đủ tin cậy để tái sử dụng làm transport/upload channel cho code backup, nhưng chưa nên reuse 1:1.
Strengths:
rcloneremote đang hoạt động thật.- quota GDrive rất lớn.
- log 7 ngày gần nhất không có error pattern.
- upload thật tới remote có metadata đối chiếu được.
- heartbeat tới Kuma đã chạy thành công trong các run gần đây.
Weaknesses / caveats:
-
Logging đang bị double-write khi chạy bằng cron. Evidence:
- script tự
tee -a "$LOG_FILE"ở line23 - cron lại
>> /opt/incomex/logs/backup-gdrive.log 2>&1ở line54 - log đêm 2026-04-03 đến 2026-04-07 có cặp
BACKUP START/BACKUP DONElặp đôi
- script tự
-
Scheduling comment không khớp runtime thật. Evidence:
- comment nói
20:00 UTC - host thực tế là
Europe/Berlin (CEST) - log cho thấy chạy
20:00:01 CEST
- comment nói
-
Không có lock chống chạy chồng. Evidence:
- trong script không có
flockhoặc lockfile - log cho thấy đã có các lần rerun manual gần nhau (
12:20:32và12:21:59ngày2026-04-08)
- trong script không có
-
Heartbeat là success-only. Evidence:
- script chỉ có push
status=up; không có nhánhstatus=down - failure sẽ hiện ra chủ yếu bằng missed heartbeat, không phải lỗi chi tiết chủ động
- script chỉ có push
-
Remote path chưa tách folder. Evidence:
rclone lsf gdrive-backup:cho thấy backup files đang trộn với các file document không liên quan ở Drive root
3.3 Kết luận phần này
Phương án “reuse current data-backup mechanism” là khả thi nếu hiểu là:
- reuse remote + credential + uploader pattern + summary + heartbeat pattern
Không khả thi nếu hiểu là:
- copy nguyên cron, nguyên folder đích, nguyên log strategy, nguyên retention số
14, và nguyên heartbeat model
4. Đề Xuất Backup Code Cho 2 Repo
4.1 Số liệu 2 repo
Evidence:
$ ssh root@38.242.240.89 'du -sh /opt/incomex/docker/nuxt-repo /opt/incomex/docker/agent-data-repo 2>/dev/null; echo "---"; du -sh /opt/incomex/docker/nuxt-repo/.git /opt/incomex/docker/agent-data-repo/.git 2>/dev/null'
23M /opt/incomex/docker/nuxt-repo
43M /opt/incomex/docker/agent-data-repo
---
6.8M /opt/incomex/docker/nuxt-repo/.git
21M /opt/incomex/docker/agent-data-repo/.git
Không tìm thấy cache/build dirs phổ biến tại thời điểm scan:
$ ssh root@38.242.240.89 'echo "NUXT EXCLUDES"; find /opt/incomex/docker/nuxt-repo -type d \( -name node_modules -o -name .nuxt -o -name .output -o -name __pycache__ -o -name .pytest_cache -o -name .mypy_cache -o -name .cache -o -name dist -o -name coverage -o -name .venv \) -print 2>/dev/null; echo "---"; echo "AGENT EXCLUDES"; find /opt/incomex/docker/agent-data-repo -type d \( -name node_modules -o -name .nuxt -o -name .output -o -name __pycache__ -o -name .pytest_cache -o -name .mypy_cache -o -name .cache -o -name dist -o -name coverage -o -name .venv \) -print 2>/dev/null'
NUXT EXCLUDES
---
AGENT EXCLUDES
Current git state:
$ ssh root@38.242.240.89 'cd /opt/incomex/docker/nuxt-repo && echo "NUXT" && git status --short --branch 2>/dev/null; echo "---"; cd /opt/incomex/docker/agent-data-repo && echo "AGENT" && git status --short --branch 2>/dev/null'
NUXT
## main...origin/main [ahead 2, behind 13]
---
AGENT
## main...origin/main [ahead 4, behind 112]
Interpretation:
- working tree hiện đang clean ở cả 2 repo
- có local commits chưa đồng bộ với origin ở cả 2 repo
- secondary backup ngoài GitHub là có giá trị thực, vì repo trên VPS chứa state không chỉ là mirror sạch của remote
4.2 Large tracked files ảnh hưởng ước lượng nén
Evidence:
$ ssh root@38.242.240.89 'echo "NUXT LARGE"; find /opt/incomex/docker/nuxt-repo -type f -size +1M -printf "%s %p\n" 2>/dev/null | sort -nr | sed -n "1,30p"; echo "---"; echo "AGENT LARGE"; find /opt/incomex/docker/agent-data-repo -type f -size +1M -printf "%s %p\n" 2>/dev/null | sort -nr | sed -n "1,40p"'
NUXT LARGE
3292202 /opt/incomex/docker/nuxt-repo/.git/objects/pack/pack-ce4cc9b180a15a8c878dafa8a70b1a0c18c053e2.pack
1655258 /opt/incomex/docker/nuxt-repo/.git/objects/pack/pack-3e9182b80cfb2108502cbea202aad2b16ffe153f.pack
1396989 /opt/incomex/docker/nuxt-repo/directus/snapshot-full.json
1060780 /opt/incomex/docker/nuxt-repo/dot/snapshots/schema-2026-03-06.json
---
AGENT LARGE
19965913 /opt/incomex/docker/agent-data-repo/.git/objects/pack/pack-6a859adab85a757f240ecc06ea48d18777b95538.pack
19285985 /opt/incomex/docker/agent-data-repo/governance/backup_terraform_20250816T062059Z.tar.gz
Các file lớn này đều đang tracked:
$ ssh root@38.242.240.89 'cd /opt/incomex/docker/agent-data-repo && git ls-files --stage governance/backup_terraform_20250816T062059Z.tar.gz 2>/dev/null; echo "---"; cd /opt/incomex/docker/nuxt-repo && git ls-files --stage directus/snapshot-full.json dot/snapshots/schema-2026-03-06.json 2>/dev/null'
100644 836852769b85b02718d1fb961aa1096ffa6f9928 0 governance/backup_terraform_20250816T062059Z.tar.gz
---
100644 c2f6e62886196d1d12e4f34e58a660ca6e5596f6 0 directus/snapshot-full.json
100644 f0649fbbe8d3dd06a373a7033118dc4a5b92f666 0 dot/snapshots/schema-2026-03-06.json
4.3 Include / exclude đề xuất
Đã xác minh:
- hiện không có
node_modules,.nuxt,.output,.venv, cache dirs phổ biến trong 2 repo .gitlà thành phần quan trọng và nên giữ
Đề xuất:
- include toàn bộ working tree của từng repo
- include
.git - exclude theo pattern phòng ngừa trong script mới:
node_modules.nuxt.output__pycache__.pytest_cache.mypy_cache.cachecoveragedist.venv
- không exclude tracked files lớn chỉ vì chúng lớn, trừ khi user xác nhận đó là historical artifact không cần cho restore
4.4 Naming đề xuất
User muốn tên file nhìn là biết repo + timestamp. Điều này đúng, nhưng format chỉ tới phút có rủi ro đụng tên khi rerun trong cùng phút.
Evidence có rerun gần nhau:
$ ssh root@38.242.240.89 'grep -nE "^BACKUP START|^BACKUP DONE|^Archive:|^PG:|^Remote backups|HEARTBEAT sent" /opt/incomex/logs/backup-gdrive.log 2>/dev/null | sed -n "70,100p"'
1331:BACKUP START: 2026-04-08 12:20:32 CEST
1426:BACKUP DONE: 2026-04-08 12:20:58 CEST
1427:Archive: 73M
1428:PG: 36M | Qdrant: 63M
1429:Remote backups: 8
1431:{"ok":true}HEARTBEAT sent to Kuma (pg-backup-gdrive)
1433:BACKUP START: 2026-04-08 12:21:59 CEST
1586:BACKUP DONE: 2026-04-08 12:22:41 CEST
Recommended naming:
web-test_YYYYMMDD-HHMMSS_ICT.tar.gzagent-data-test_YYYYMMDD-HHMMSS_ICT.tar.gz
Nếu user bắt buộc muốn format tới phút:
- dùng
web-test_YYYYMMDD-HHMM_ICT.tar.gz - nhưng phải thêm suffix retry khi collision (
-r01,-r02)
4.5 Remote path đề xuất
Không nên đẩy code backup vào Drive root như data-backup hiện tại.
Recommended remote layout:
gdrive-backup:incomex-code-backups/web-test/gdrive-backup:incomex-code-backups/agent-data-test/
Lý do:
- cleanup retention theo repo an toàn hơn
- user nhìn Drive dễ hơn
- không trộn code backup với data backup và file document cá nhân
4.6 Ước lượng kích thước tar.gz
Không tạo tarball thật trong mission này; đây là suy luận từ số liệu đo được.
Estimated archive size:
| Repo | Measured size | Important already-compressed content | Estimated .tar.gz |
Confidence |
|---|---|---|---|---|
web-test |
23M |
.git packs ~4.9M; large JSON snapshots ~2.4M |
10M–14M |
Medium |
agent-data-test |
43M |
.git pack ~20M; tracked backup_terraform_*.tar.gz ~19M |
39M–42M |
Medium-High |
Estimated combined per run:
- khoảng
49M–56M
Estimated storage for retention:
4 runs/dayx14 days≈56archives/repo- combined retained size khoảng
2.8G–3.1G - so với
Free: 4.986 TiB, mức này là rất nhỏ
4.7 Script flow đề xuất cho Bước 2
Proposed new script:
/opt/incomex/scripts/code-backup-to-gdrive.sh
Proposed flow:
flock/lockfile để chặn chạy chồng- xác định timestamp theo
Asia/Ho_Chi_Minhđể tên file đúng ICT - với mỗi repo:
- capture
git rev-parse HEAD - capture
git status --short --branch - copy repo vào temp workdir với exclude patterns
- tạo archive
.tar.gz - upload bằng
rclone copyqua remote hiện có - verify remote metadata bằng
rclone lslvà so exact size với local - keep only latest local archive của repo đó
- cleanup remote theo retention riêng của repo
- push heartbeat repo-specific
- capture
- ghi summary cuối cùng vào log
4.8 Heartbeat recommendation
Chọn 2 monitor riêng, không chọn 1 monitor chung.
Lý do:
web-testvàagent-data-testlà 2 artifact độc lập- nếu 1 repo upload fail còn repo kia thành công, 1 monitor chung sẽ mơ hồ
- 2 monitor cho phép alert rõ repo nào lỗi
Recommended monitor names/push IDs:
code-backup-web-testcode-backup-agent-data-test
4.9 Cron recommendation
Đã xác minh mapping theo runtime hiện tại (CEST):
$ ssh root@38.242.240.89 'for t in "2026-04-08 08:00" "2026-04-08 12:00" "2026-04-08 15:00" "2026-04-08 20:00"; do ts=$(TZ=Asia/Ho_Chi_Minh date -d "$t" +%s 2>/dev/null) || continue; printf "%s ICT => " "$t"; TZ=Europe/Berlin date -d @${ts} +"%Y-%m-%d %H:%M:%S %Z (%z)"; done'
2026-04-08 08:00 ICT => 2026-04-08 03:00:00 CEST (+0200)
2026-04-08 12:00 ICT => 2026-04-08 07:00:00 CEST (+0200)
2026-04-08 15:00 ICT => 2026-04-08 10:00:00 CEST (+0200)
2026-04-08 20:00 ICT => 2026-04-08 15:00:00 CEST (+0200)
Recommended final cron, nếu host cron support CRON_TZ:
CRON_TZ=Asia/Ho_Chi_Minh
0 8,12,15,20 * * * /opt/incomex/scripts/code-backup-to-gdrive.sh >> /opt/incomex/logs/code-backup-gdrive.log 2>&1
Fallback only if CRON_TZ không support và deploy khi host đang CEST:
0 3,7,10,15 * * * /opt/incomex/scripts/code-backup-to-gdrive.sh >> /opt/incomex/logs/code-backup-gdrive.log 2>&1
Lưu ý:
- fallback này sẽ lệch khi Berlin đổi
CEST -> CET - vì vậy
CRON_TZ=Asia/Ho_Chi_Minhlà hướng nên ưu tiên verify trong Bước 2
4.10 Retention recommendation
Data-backup hiện dùng 14 remote copies, nhưng với 4 runs/day thì 14 chỉ giữ được 3.5 ngày.
Recommended:
- remote:
56archives / repo (14 ngày) - local:
1latest archive / repo
Lý do:
- dung lượng rất nhỏ so với quota
- restore point dày hơn
- vẫn đơn giản để cleanup
5. Rủi Ro Và Giảm Thiểu
5.1 Dung lượng GDrive
Đã xác minh: đủ.
Evidence:
Free: 4.986 TiB
Mitigation:
- không cần tối ưu sớm vì dung lượng dự kiến chỉ cỡ
~3 GBcho retention14 ngày
5.2 Backup khi repo đang thay đổi
Risk:
- tar trực tiếp trên live repo có thể chụp state lẫn trước/sau khi file đổi
Mitigation đề xuất:
- staging copy sang temp workdir trước khi nén
- log lại
HEADvàgit status - nếu
HEADđổi trong lúc chạy, fail run hoặc retry có kiểm soát
5.3 Agent commit đúng lúc backup chạy
Risk:
- archive có thể không tương ứng với một revision nhất quán
Mitigation đề xuất:
- lock chống chạy chồng
- chụp metadata
HEAD before/after - nếu
HEAD before != HEAD after, mark run failed
5.4 Credential rotate
Risk:
- code backup và data backup cùng phụ thuộc một
rcloneconfig; rotate sai sẽ làm hỏng cả 2
Mitigation đề xuất:
- giữ script code backup tách riêng data backup
- sau mỗi credential rotation, manual run 1 lần trong Bước 2 để verify
- monitor riêng cho code backup giúp thấy ngay tác động
5.5 Trùng tên file
Risk:
- naming tới phút có thể collision khi rerun trong cùng phút
Mitigation đề xuất:
- ưu tiên
HHMMSS - hoặc minute + suffix retry
5.6 So sánh 4 lần/ngày vs mỗi lần git commit
Khuyến nghị: 4 lần/ngày.
Lý do:
- reuse tốt hơn cơ chế cron + Kuma đang có
- tải thấp, dễ dự đoán, dễ monitor
- không phụ thuộc việc mọi thay đổi đều đi qua commit hook
- per-commit dễ spam archive khi rewrite lịch sử hoặc commit dồn
- per-commit cũng không bảo vệ uncommitted-but-important working tree nếu chỉ backup theo commit event
6. Kế Hoạch Bước 2
6.1 Thứ tự triển khai đề xuất
- Tạo script mới
/opt/incomex/scripts/code-backup-to-gdrive.sh - Tạo folder remote logical path cho từng repo
- Thêm lock, ICT timestamp, repo manifest, upload verify, cleanup retention
- Tạo
2Kuma push IDs riêng - Chạy tay
1lần - Verify có
2file thật trên GDrive, đúng tên, đúng size, có log, có heartbeat - Chỉ sau khi manual run pass mới thêm cron
- Quan sát ít nhất
1scheduled run sau đó
6.2 Rollback plan
Rollback của Bước 2 nên rất đơn giản:
- không đụng vào
backup-to-gdrive.shhiện có - nếu manual run fail: sửa script mới, chưa add cron
- nếu scheduled run gây lỗi/noise: remove only new cron line(s), giữ lại log để postmortem
- không ảnh hưởng data-backup hiện tại
6.3 Verify checklist cho Bước 2
code-backup-to-gdrive.shchạy tay exit0- trên GDrive thấy đủ
2file thật - tên file đúng repo + timestamp ICT
rclone lslsize khớp localstat -c%s- log có summary từng repo
- cả
2heartbeat lên Kuma đúng monitor - cleanup local giữ đúng
1latest / repo - cleanup remote giữ đúng retention đã chọn
7. Unknowns
- Chưa verify được trực tiếp
CRON_TZsupport trên host;man 5 crontab | grep CRON_TZkhông trả output, nên đây vẫn là mục cần xác minh trong Bước 2. - Chưa tạo tarball thật trong mission này, nên archive size chỉ là estimate.
- Chưa inspect trực tiếp Kuma UI/config; mới xác minh được push pattern qua script và log.
- File tracked
governance/backup_terraform_20250816T062059Z.tar.gztrongagent-data-repocó thể là historical artifact; chưa đủ bằng chứng để đề xuất exclude.
8. Appendix: Commands + Outputs
A. Script metadata
$ ssh root@38.242.240.89 'ls -l /opt/incomex/scripts/backup-to-gdrive.sh 2>/dev/null; echo "---"; stat -c "%n %U:%G %a %s bytes %y" /opt/incomex/scripts/backup-to-gdrive.sh 2>/dev/null'
-rwxr-xr-x 1 root root 5646 Apr 8 12:19 /opt/incomex/scripts/backup-to-gdrive.sh
---
/opt/incomex/scripts/backup-to-gdrive.sh root:root 755 5646 bytes 2026-04-08 12:19:31.748304978 +0200
B. Backup log summary window [excerpt]
$ ssh root@38.242.240.89 'grep -nE "^BACKUP START|^Archive:|^PG:|^Remote backups|HEARTBEAT sent|^BACKUP DONE" /opt/incomex/logs/backup-gdrive.log 2>/dev/null | sed -n "1,120p"'
2:BACKUP START: 2026-04-03 10:33:42 CEST
58:BACKUP DONE: 2026-04-03 10:33:58 CEST
59:Archive: 32M
60:PG: 34M | Qdrant: 4.0K
61:Remote backups: 1
64:BACKUP START: 2026-04-03 10:43:07 CEST
135:BACKUP DONE: 2026-04-03 10:43:31 CEST
136:Archive: 77M
137:PG: 34M | Qdrant: 66M
138:Remote backups: 2
141:BACKUP START: 2026-04-03 20:00:01 CEST
145:BACKUP START: 2026-04-03 20:00:01 CEST
...
1586:BACKUP DONE: 2026-04-08 12:22:41 CEST
1587:Archive: 73M
1588:PG: 36M | Qdrant: 63M
1589:Remote backups: 9
1591:{"ok":true}HEARTBEAT sent to Kuma (pg-backup-gdrive)
C. Repo top-level size highlights [excerpt]
$ ssh root@38.242.240.89 'echo "NUXT TOP"; du -sh /opt/incomex/docker/nuxt-repo/* /opt/incomex/docker/nuxt-repo/.[!.]* /opt/incomex/docker/nuxt-repo/..?* 2>/dev/null | sort -h | tail -n 40; echo "---"; echo "AGENT TOP"; du -sh /opt/incomex/docker/agent-data-repo/* /opt/incomex/docker/agent-data-repo/.[!.]* /opt/incomex/docker/agent-data-repo/..?* 2>/dev/null | sort -h | tail -n 40'
NUXT TOP
...
2.2M /opt/incomex/docker/nuxt-repo/directus
2.9M /opt/incomex/docker/nuxt-repo/dot
3.1M /opt/incomex/docker/nuxt-repo/reports
5.3M /opt/incomex/docker/nuxt-repo/web
6.8M /opt/incomex/docker/nuxt-repo/.git
---
AGENT TOP
...
496K /opt/incomex/docker/agent-data-repo/tests
19M /opt/incomex/docker/agent-data-repo/governance
21M /opt/incomex/docker/agent-data-repo/.git
D. Local disk headroom
$ ssh root@38.242.240.89 'df -h /opt/incomex/backups /opt/incomex/docker 2>/dev/null'
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 96G 38G 59G 39% /
/dev/sda1 96G 38G 59G 39% /