256 lines
5.9 KiB
Markdown
256 lines
5.9 KiB
Markdown
|
|
# 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.
|
||
|
|
|
||
|
|
## Recommended Setup
|
||
|
|
|
||
|
|
Use three directories on the VPS:
|
||
|
|
|
||
|
|
```txt
|
||
|
|
/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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd /srv/content-editor-v2/repo.git
|
||
|
|
git init --bare
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Create the live checkout
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
export CONTENT_ROOT=/srv/content-editor-v2/shared/content
|
||
|
|
```
|
||
|
|
|
||
|
|
If you already have good content locally, copy it once:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cp -R /srv/content-editor-v2/app/content/. /srv/content-editor-v2/shared/content/
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5. Install Node dependencies in the live app dir
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd /srv/content-editor-v2/app
|
||
|
|
npm install
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6. Add a process manager
|
||
|
|
|
||
|
|
PM2 is the easiest option:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
npm install -g pm2
|
||
|
|
```
|
||
|
|
|
||
|
|
Then start the app:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```txt
|
||
|
|
/srv/content-editor-v2/repo.git/hooks/post-receive
|
||
|
|
```
|
||
|
|
|
||
|
|
Use this:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
#!/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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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:
|
||
|
|
|
||
|
|
```powershell
|
||
|
|
git init
|
||
|
|
git add .
|
||
|
|
git commit -m "Initial project import"
|
||
|
|
```
|
||
|
|
|
||
|
|
Add the VPS as a remote:
|
||
|
|
|
||
|
|
```powershell
|
||
|
|
git remote add vps ssh://YOUR_USER@YOUR_HOST:/srv/content-editor-v2/repo.git
|
||
|
|
```
|
||
|
|
|
||
|
|
Then deploy with:
|
||
|
|
|
||
|
|
```powershell
|
||
|
|
git push vps master
|
||
|
|
```
|
||
|
|
|
||
|
|
Or `main`, if that is your branch name.
|
||
|
|
|
||
|
|
## Windows-Friendly Deploy Helper
|
||
|
|
|
||
|
|
This repo includes a PowerShell helper:
|
||
|
|
|
||
|
|
```powershell
|
||
|
|
.\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:
|
||
|
|
|
||
|
|
```nginx
|
||
|
|
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.
|
||
|
|
|
||
|
|
## Recommended Workflow
|
||
|
|
|
||
|
|
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
|