KB-348B
P10D-2D Deploy Discovery — Phase A only (STOP for decision)
9 min read Revision 1
p10ddeploydiscoverystop
P10D-2D — Deploy Discovery Report (Phase A only — STOP)
Date: 2026-04-30 Agent: Claude (Opus 4.7) via SSH contabo Verdict: DISCOVERY_ONLY_STOP — multiple mechanisms identified; awaiting user decision before activating.
Gate 0
| Item | Value |
|---|---|
| Control host | Nguyens-MacBook-Air.local (nmhuyen) |
| VPS | vmi3080463.contaboserver.net |
| Directus DB | directus/directus (PostgreSQL) |
| Web repo | /opt/incomex/docker/nuxt-repo |
| Git HEAD | 5ce3437bf78db9989eafec14216fb5d2f2a26801 (target — already checked out) |
| HEAD commit | "P10D: add parallel TAC official laws screen" — 2026-04-30 11:15 +0200 |
| Working tree | clean |
Phase A — Cầu dao (deploy mechanism) inventory
A1. Container runtime
- Container:
incomex-nuxt, imagenuxt-ssr-local:s174, status: Up 3 weeks (healthy) - Bind mount:
/opt/incomex/deploys/nuxt-output→/app/.output(rw) - CMD:
node .output/server/index.mjs(reads from bind mount, NOT from image layer) - WORKDIR:
/app
A2. Bind mount state — what's currently serving
/opt/incomex/deploys/nuxt-output/server/index.mjsmtime: 2026-04-02 16:37- 28 days old; predates HEAD commit
5ce3437(2026-04-30) by 28 days - Backups present:
nuxt-output.prev: 2026-04-02 15:57 (same-day previous)nuxt-output.prev2: 2026-03-08 05:05
A3. Local images on VPS
| Image | Created marker |
|---|---|
nuxt-ssr-local:s174 (currently running) |
tag = s174 |
nuxt-ssr-local:latest |
exists |
docker-nuxt:latest |
exists |
asia-southeast1-docker.pkg.dev/.../nuxt-ssr:latest |
LEGACY (Artifact Registry API disabled S154) |
A4. Dockerfile (nuxt-repo/web/Dockerfile)
- Two-stage:
node:20-alpinebuilder runspnpm install --frozen-lockfile+pnpm run build - Production stage copies
/app/.outputfrom builder..output/IS baked into image at/app/.output. - However: bind mount in compose shadows the image's
/app/.outputat runtime → only bind mount contents matter.
A5. Build tools on VPS host
pnpm: NOT FOUNDnode: NOT FOUNDdocker: present- Disk: 55G free; RAM: 8.2G available — sufficient for docker build
A6. Compose file (/opt/incomex/docker/docker-compose.yml)
- nuxt service hardcoded to
image: nuxt-ssr-local:s174(note: differs frominfra/docker/docker-compose.ymlin repo which uses${NUXT_SSR_IMAGE:-...artifact-registry...}) - Bind mount
/opt/incomex/deploys/nuxt-output:/app/.output✓ - The deployed compose file has DRIFTED from repo
infra/docker/docker-compose.yml(image pin tonuxt-ssr-local:s174)
A7. Discovered deploy paths
Cầu dao 1 — GitHub Actions deploy-vps.yml job deploy-direct (DEPLOY_MODE=direct)
- Active gate:
vars.DEPLOY_MODE == 'direct' - Mechanism: GitHub runner builds
.output/(pnpm) →rsync -avz --delete web/.output/ → /opt/incomex/deploys/nuxt-output/→docker compose restart nuxt→ health check → auto-rollback tonuxt-output.prevon failure. - Also rsyncs: dot/bin, dot/config, scripts/integrity, contracts, sql migrations.
- Also overwrites:
/opt/incomex/docker/docker-compose.ymlfrom repoinfra/docker/docker-compose.yml(this would FLIP the image away fromnuxt-ssr-local:s174→ would try to pull from disabled Artifact Registry → BREAK). - Trigger: workflow_run after Nuxt CI passes on main, or workflow_dispatch.
- Evidence of past use: matches the
.output/produced 2026-04-02 + presence ofnuxt-output.prev+ DOT scripts on VPS.
Cầu dao 2 — GitHub Actions deploy-vps.yml job deploy-artifact (DEPLOY_MODE != direct)
- Status: LEGACY — Artifact Registry API disabled S154 (per workflow comment).
- Image used at runtime:
nuxt-ssr-local:s174(local), NOTasia-southeast1-docker.pkg.dev/.... So this path is NOT what produced current state. - Verdict: inactive cầu dao.
Cầu dao 3 — Manual host build (pnpm/node on VPS)
- Status: NOT POSSIBLE — host has no pnpm/node.
Cầu dao 4 — Manual docker-build-on-VPS + extract .output/ + rsync to bind mount + restart
- Status: technically feasible (docker present, Dockerfile bakes
.output); has not been used historically (no evidence in deploy logs/scripts). - Risk: untested, off-pipeline, drifts from CI build context (env vars
NUXT_PUBLIC_SITE_URL, etc., are baked at build time per Dockerfile ARG).
A8. Cầu dao table
| # | Cầu dao | Evidence | Last used | Đang active? | Activate 5ce3437? |
|---|---|---|---|---|---|
| 1 | GHA deploy-direct (DEPLOY_MODE=direct) |
matches .output/ 2026-04-02; rsync target = bind mount; auto-rollback uses .prev |
2026-04-02 (28d ago) | YES (pipeline) — but compose-file overwrite step would replace pinned nuxt-ssr-local:s174 with ${NUXT_SSR_IMAGE:-...artifact registry...} reference → risk of breaking on next restart |
YES — IF you accept GHA run + accept that compose drift would be re-introduced |
| 2 | GHA deploy-artifact (legacy) |
Artifact Registry disabled S154; running image is local | unknown / never on this stack | NO (inactive) | NO (registry disabled) |
| 3 | Manual host pnpm build | no pnpm/node on host | never | NO | NO |
| 4 | Manual VPS docker build + extract + rsync + restart |
feasible; no prior evidence | never | NO (not in pipeline) | YES technically — but off-pipeline and untested for this stack |
Why I'm stopping
Per prompt rule:
Nhiều cầu dao → liệt kê + STOP báo cáo xin ý kiến. Không tự chọn.
Two viable cầu dao to activate 5ce3437:
- Cầu dao 1 (GHA direct, established): correct mechanism, but I cannot trigger GitHub Actions from this SSH session — that's a GitHub-side action, not a VPS-side action. Also the workflow's compose-overwrite step is a side effect that wasn't authorized in this prompt.
- Cầu dao 4 (manual VPS docker build): I CAN execute from SSH, but it's off-pipeline. The prompt says "Path 2 (build .output → rsync to nuxt-output → docker restart) chỉ được bật nếu Phase A xác nhận đây là cầu dao duy nhất đang active". It is not the cầu dao that has been active — Cầu dao 1 has been. So per the rule I should not auto-pick it.
Additional concern: the deployed /opt/incomex/docker/docker-compose.yml has drifted from repo infra/docker/docker-compose.yml (image pin). The GHA pipeline would re-sync repo compose and break the pin. This is a separate compose-gate question.
Decision needed (from user)
Pick one and confirm:
- Trigger GHA
deploy-vps.yml(workflow_dispatch) — proper pipeline path. User must trigger from GitHub UI. Compose-overwrite side effect must be accepted or pre-resolved (the repo'sinfra/docker/docker-compose.ymlwould need its image line aligned withnuxt-ssr-local:s174first, or you accept the registry pull will fail and cause auto-rollback). - Manual VPS docker-build path — I'd run on VPS:
cd /opt/incomex/docker/nuxt-repo && docker build -t nuxt-ssr-local:p10d -f web/Dockerfile web/- extract
/app/.outputfrom image into/tmp/p10d-output - backup current
/opt/incomex/deploys/nuxt-output→nuxt-output.bak.p10d-<ts> rsync -a --delete /tmp/p10d-output/ /opt/incomex/deploys/nuxt-output/docker restart incomex-nuxt- run Phase C verifies; rollback on fail
- Risks: (a) Dockerfile build args pin
NUXT_PUBLIC_SITE_URL/NUXT_PUBLIC_DIRECTUS_URLat build-time — needs--build-argto match VPS prod URLs (https://vps.incomexsaigoncorp.vn/https://directus.incomexsaigoncorp.vn); (b) build will pull dependencies through host network; (c) image tagnuxt-ssr-local:p10d≠ running tags174so container keeps running offs174image but reads new.output/from bind mount — same as direct-deploy semantics.
- Defer — investigate compose drift (
docker-compose.ymlvsinfra/docker/docker-compose.yml) first.
I have NOT executed Phase B or Phase C. No backup created. No file modified on VPS.
Commands executed (read-only Phase A)
ssh contabo 'hostname -f && docker exec postgres psql -U directus -d directus -tAc "SELECT current_database() || chr(47) || current_user;"'
ssh contabo 'docker ps --format ... | grep -i nuxt'
ssh contabo 'docker inspect incomex-nuxt --format ... .Mounts/.Image/.Cmd/.WorkingDir'
ssh contabo 'ls/stat /opt/incomex/deploys/nuxt-output{,.prev,.prev2}/'
ssh contabo 'docker images | grep -i nuxt'
ssh contabo 'find /opt/incomex -maxdepth 5 ... | xargs grep -l ...'
ssh contabo 'grep -A 30 "^[[:space:]]*nuxt:" /opt/incomex/docker/docker-compose.yml'
ssh contabo 'cat /opt/incomex/docker/nuxt-repo/web/Dockerfile'
ssh contabo 'which pnpm node docker; df -h; free -h'
ssh contabo 'cd .../nuxt-repo && git log/status'
ssh contabo 'cat .../deploy-vps.yml'
ssh contabo 'grep -A 25 "nuxt" .../infra/docker/docker-compose.yml'
No mutations. No DB writes. No file changes.