Worldshaper/docs/VPS_DEPLOYMENT.md
2026-06-26 18:18:14 -04:00

5.9 KiB

VPS Deployment

This project is much easier to manage if we stop treating your desktop folder as the "deployment artifact" and instead treat the app like a normal repository with a normal deploy target.

Use three directories on the VPS:

/srv/content-editor-v2/
  repo.git/          # bare git repo, receives pushes
  app/               # checked out working tree, built + run from here
  shared/
    content/         # persistent content, survives redeploys

Why this shape works:

  • repo.git is the deployment remote.
  • app/ is the live checkout your process manager runs.
  • shared/content/ keeps your authored data outside the release tree.

That means redeploying code does not overwrite your content.

Best Fit For This Project

This app is not just a static front-end build. It also has:

  • server.js for the API
  • content/ for writable data
  • docs/ for wiki assets
  • dist/ for the built front-end

Because of that, the cleanest production model is:

  1. Run server.js on the VPS from a stable app directory.
  2. Let server.js serve dist/ and /wiki.
  3. Put Nginx or your hosting panel in front of it as a reverse proxy.

That is better than manually uploading a folder to a public web root.

One-Time VPS Setup

Run these on the VPS.

1. Create directories

sudo mkdir -p /srv/content-editor-v2/repo.git
sudo mkdir -p /srv/content-editor-v2/app
sudo mkdir -p /srv/content-editor-v2/shared/content

2. Initialize the bare repo

cd /srv/content-editor-v2/repo.git
git init --bare

3. Create the live checkout

git --work-tree=/srv/content-editor-v2/app --git-dir=/srv/content-editor-v2/repo.git checkout -f

If this is the first time and there is no pushed branch yet, that checkout will not fully populate until the first push.

4. Keep content outside the app tree

This project already supports CONTENT_ROOT.

Production should use:

export CONTENT_ROOT=/srv/content-editor-v2/shared/content

If you already have good content locally, copy it once:

cp -R /srv/content-editor-v2/app/content/. /srv/content-editor-v2/shared/content/

5. Install Node dependencies in the live app dir

cd /srv/content-editor-v2/app
npm install

6. Add a process manager

PM2 is the easiest option:

npm install -g pm2

Then start the app:

cd /srv/content-editor-v2/app
CONTENT_ROOT=/srv/content-editor-v2/shared/content PORT=5180 pm2 start server.js --name content-editor-v2
pm2 save

Automatic Deploy On Push

The cleanest version is a post-receive hook in the bare repo.

Create:

/srv/content-editor-v2/repo.git/hooks/post-receive

Use this:

#!/usr/bin/env bash
set -euo pipefail

APP_DIR="/srv/content-editor-v2/app"
GIT_DIR="/srv/content-editor-v2/repo.git"
CONTENT_ROOT="/srv/content-editor-v2/shared/content"
PORT="5180"

echo "[deploy] checking out latest code"
git --work-tree="$APP_DIR" --git-dir="$GIT_DIR" checkout -f

cd "$APP_DIR"

echo "[deploy] installing dependencies"
npm install

echo "[deploy] validating content"
npm run validate:content

echo "[deploy] building"
npm run build

echo "[deploy] reloading app"
CONTENT_ROOT="$CONTENT_ROOT" PORT="$PORT" pm2 restart content-editor-v2 || \
CONTENT_ROOT="$CONTENT_ROOT" PORT="$PORT" pm2 start server.js --name content-editor-v2

Make it executable:

chmod +x /srv/content-editor-v2/repo.git/hooks/post-receive

Local Machine Setup

On your home computer, initialize git in this project if you have not already:

git init
git add .
git commit -m "Initial project import"

Add the VPS as a remote:

git remote add vps ssh://YOUR_USER@YOUR_HOST:/srv/content-editor-v2/repo.git

Then deploy with:

git push vps master

Or main, if that is your branch name.

Windows-Friendly Deploy Helper

This repo includes a PowerShell helper:

.\scripts\deploy-vps.ps1 -Remote vps -Branch master

What it does:

  1. Runs npm run validate:content
  2. Runs npm run build
  3. Pushes your branch to the chosen remote

That means your local build fails before a bad deploy reaches the server.

Reverse Proxy

If your VPS uses Nginx, proxy your public domain to the Node app:

server {
    listen 80;
    server_name your-domain.com;

    location / {
        proxy_pass http://127.0.0.1:5180;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

If You Really Need A Separate Hosted Directory

If your panel requires a specific public directory, there are two workable options:

Option A: still run Node, proxy to it

Best option. Keep the app in /srv/content-editor-v2/app and point the panel or reverse proxy at the Node port.

Option B: copy only dist/ into a public web root

This works only if you separate the API and front-end hosting model. For this project today, that is not the clean default because server.js also serves static files and wiki docs.

Day to day:

  1. Work locally in a normal git repo.
  2. Commit changes.
  3. Run .\scripts\deploy-vps.ps1.
  4. Let the VPS hook pull, build, validate, and restart.

That replaces:

  • random desktop folder
  • manual upload
  • dragging files through a VPS file manager
  • wondering what version is actually live

What I Recommend You Do Next

  1. Initialize this folder as a git repo.
  2. Set up the bare repo + app + shared content structure on the VPS.
  3. Put the post-receive hook in place.
  4. Start the app under PM2.
  5. Switch deployment to git push vps <branch>.

Once you want, we can also add:

  • a production .env loader
  • branch-based staging vs production deploys
  • backup/restore scripts for content/
  • a one-command VPS bootstrap script