VMS Server Setup Wizard V7 — deploy = GitHub only, root = PM2

Simple, project-specific steps for Ubuntu VPS/Oracle. Fill the details once, then execute one step at a time.

Important: Never paste your SSH private key, GitHub password or server password here — the wizard only writes instructions for those.
The Environment variables section below does take real values, because they have to end up inside ecosystem.config.js on the server. This page is a plain local file with no network calls, so nothing you type leaves your browser — just don't screen-share or save the generated page afterwards.
Who does what in this version
deploy exists only for GitHub access — it holds the repo deploy key and runs git clone / fetch / reset, nothing else.
root does everything else — Node.js, PM2 (pm2 status, pm2 logs, pm2 restart), Nginx, SSL, and it is the user GitHub Actions logs in as.
your Mac only creates Key A.

1. Where are you deploying?

How to get it: VPS/Oracle Console → Compute Instance → Public IPv4.
Just a name for the server. Example: vms-prod.

2. What are you deploying?

Server folder becomes /var/www/your-folder. Use lowercase + hyphens.
DNS must point this domain to the server IP before SSL.
From github.com/OWNER/REPO.

3. App settings

For Node/Next only. Pick a unique port per app: 3001, 3002, 3003... Do NOT open it in UFW.
Only Node API. Use the file your project actually starts.
React/Vite only, ignored for Node and Next.js. This decides which folder Nginx serves and which path gets long-term caching. Using a custom outDir? Pick the closest one and rename the folder in the generated Nginx config.
Usually npm run build. If your repo has no build step, tell me before using the generated command.
Optional API check, e.g. /health. If you don't have one, leave blank.
Nginx's own default is only 1 MB — anything bigger is rejected with a 413 before it ever reaches your app. Set this to the largest file your API accepts. Use 0 to remove the limit entirely.

4. Environment variables

These are written straight into the env block of ecosystem.config.js on the server, so there is no .env file to create by hand and nothing to add manually afterwards.
NODE_ENV and PORT are filled in automatically from the settings above — list only the rest here (database URL, JWT secret, API keys, SMTP…).
A key must look like DATABASE_URL — letters, digits and underscores, never starting with a digit. Blank rows are ignored. Paste each value exactly as the app expects it; quoting and escaping are handled for you. Adding PORT or NODE_ENV here overrides the values from the sections above.

5. SSH / GitHub — only 2 keys, no confusion

Key A lets GitHub Actions log into the server as root, because deployment has to restart PM2 and reload Nginx. Key B lives with the deploy user and is the only thing that talks to your GitHub repository. The two keys are separate and never mix.
Name only. Example: vms-lms-api-actions. Private key goes to GitHub Secret; public key goes to /root/.ssh/authorized_keys.
Name only. Example: vms-lms-api-repo. Created inside /home/deploy/.ssh; public key goes to GitHub Repo → Settings → Deploy keys.
A local nickname used only in the deploy user's SSH config. Example: github-vms-lms.
Used only to hold Key B and run git. PM2 / logs / restart / Nginx all stay on root. Created with adduser the normal way — you reach it from root with su - deploy.

6. Generate

You will get a numbered checklist. Do Step 1 → check output → then Step 2. Don't paste the whole runbook into the terminal at once.