# Arvan VPS — Deploy Guide Battle-tested steps for deploying to the Arvan cloud server. Written after a deploy where we hit every trap below — follow this and they won't bite again. ## Server facts | Thing | Value | |---|---| | SSH host (inbound) | `94.101.187.12` | | **Egress IP** (outbound — for API whitelists, e.g. Asian Metal) | **`31.171.101.234`** ⚠️ different from SSH IP | | SSH user | `root` **only** (key auth) — `ubuntu` does NOT work | | SSH key | `~/.ssh/yz_deploy_ed25519` (public key is in `root`'s `authorized_keys`) | | Code on server | `/root/statista` — **plain copied tree, NOT a git repo** | | Live config | `/root/statista/deploy/.env` (root-only, **untracked — never in git**) | | Stack | `docker compose -f deploy/docker-compose.yml` → `nginx` + `frontend` (:3000) + `backend` (:8000) | | Public URL | `http://94.101.187.12` (nginx on :80) | ## The deploy (copy-paste, run from your local repo) ```bash KEY=~/.ssh/yz_deploy_ed25519 BR=deploy-vps-restructure # branch to deploy # 1) ship the committed tree (NOT a git pull — server isn't a git repo). # git archive only includes tracked files, so deploy/.env + data are untouched. git archive --format=tar "$BR" | \ ssh -i "$KEY" root@94.101.187.12 'cd /root/statista && tar -xf -' # 2) rebuild images + recreate containers ssh -i "$KEY" root@94.101.187.12 \ 'cd /root/statista && docker compose -f deploy/docker-compose.yml up -d --build' # 3) ⚠️ ALWAYS restart nginx afterwards (see Trap #2) ssh -i "$KEY" root@94.101.187.12 \ 'cd /root/statista && docker compose -f deploy/docker-compose.yml restart nginx' # 4) verify (all must be 200) ssh -i "$KEY" root@94.101.187.12 \ 'for p in / /manifest.webmanifest /sw.js /api/currency; do echo "$p -> $(curl -s -o /dev/null -w "%{http_code}" http://localhost$p)"; done' ``` Commit + push first (`git push origin deploy-vps-restructure`) if you want the GitHub copy current — but the archive deploys your **local** committed tree regardless, so step 1 is the source of truth for the server. ## Traps we hit (and the fixes) ### Trap #1 — Server is NOT a git repo `/root/statista` was copied (rsync/scp), not cloned. `git pull` fails with "not a git repository". **Fix:** deploy with `git archive | ssh … tar -xf -` (above). It overwrites only tracked files; untracked `deploy/.env`, the SQLite DB, and `/data` reports are left alone. ### Trap #2 — nginx 502 after rebuild (stale upstream IP) `up -d --build` recreates `frontend`/`backend` with **new Docker IPs**, but nginx keeps running and caches the **old** IPs → every route returns **502** even though the containers are healthy and listening. **Fix:** always `docker compose restart nginx` as the last step. (Confirmed: frontend logs show `Listening on :3000` while nginx still 502s — that's this, not an app bug.) ### Trap #3 — SSH access - Only **`root` + key** works. `ubuntu` is rejected. - **Password login is unreliable via Arvan's web console** — the noVNC keyboard layout mistypes mixed-case/symbol passwords, so a *correct* password reads as "wrong". Always SSH **from a real terminal**, not the web console. If you must reset the password (panel) use **all-lowercase + digits** and **reboot** after (resets often don't apply until restart). - To grant a new machine access: append its public key to `/root/.ssh/authorized_keys` (don't rebuild the server to inject keys). ### Trap #4 — never "Rebuild / reinstall OS" Rebuild/reinstall **wipes the disk** (DB + reports gone) and can **change the IP** (breaks API whitelists). Updates go through the deploy steps above — **only ever `reboot`**, never rebuild. Reboots keep IP + data. ### Trap #5 — egress ≠ inbound IP APIs that whitelist by IP (Asian Metal: "1 API = 1 IP") see the **egress IP `31.171.101.234`**, not the SSH IP. Verify any time with: `ssh -i $KEY root@94.101.187.12 'curl -s https://api.ipify.org'` ## Rollback `up -d --build` keeps the previous images. If a deploy is bad: ```bash ssh -i $KEY root@94.101.187.12 \ 'cd /root/statista && docker compose -f deploy/docker-compose.yml down && \ docker compose -f deploy/docker-compose.yml up -d && \ docker compose -f deploy/docker-compose.yml restart nginx' ``` For a code rollback, `git archive` an earlier commit/tag in step 1, then rebuild. ## Health check anytime ```bash ssh -i $KEY root@94.101.187.12 'docker compose -f /root/statista/deploy/docker-compose.yml ps' ssh -i $KEY root@94.101.187.12 'docker logs deploy-frontend-1 --tail 20' ssh -i $KEY root@94.101.187.12 'docker logs deploy-backend-1 --tail 20' ```