源文件:01-overview-and-architecture.md

# 01. Overview and Architecture

# Goal

Provide a standard, repeatable way to host multiple Node.js applications on one Rocky/EL9 VPS with:

  • multiple Node versions
  • TLS via Nginx
  • systemd-managed processes
  • release-based deployments
  • shared env files
  • constrained CI/CD permissions

# Live architecture on this VPS


Internet


  -> Nginx :80/:443


  -> proxy_pass http://127.0.0.1:<node-port>


  -> systemd service node-site@<domain>.service


  -> /usr/local/bin/node-site-runner <domain>


  -> /usr/local/bin/node-with-fnm --version <18|20|22>


  -> npm start


# Deployment architecture


repo (/srv/git/<domain>)


  -> deploy-node-release


  -> new release under /var/www/nodeXX/<domain>/releases/<timestamp>


  -> shared links restored (.env.local, etc.)


  -> current symlink switched


  -> service restart


  -> healthcheck


# Bootstrap architecture

For a brand-new domain, the toolkit can also bootstrap a starter site in one step:


bootstrap-node-site


  -> create /srv/git/<domain>


  -> write starter package.json/server.js/.node-version/.gitignore


  -> generate package-lock.json with the selected Node version


  -> git init + initial commit


  -> create-node-site


  -> init-node-shared-env


  -> cicd-deploy-node-site


This is useful when you want a fresh Node site to become reachable immediately, before replacing the starter app with a real repository.

# Release layout

Each Node site follows:


/var/www/node22/example.com/


  current -> releases/<timestamp>


  releases/


  shared/


# Why this is used

  • allows atomic-ish deploys
  • keeps old releases for rollback inspection
  • separates persistent data from code
  • matches the PHP hosting approach already used on this VPS

# Shared env design

Both runtime and build/deploy phases now auto-load:

  • .env
  • .env.local

This means secrets and environment-specific configuration can live in:

  • /var/www/nodeXX/<domain>/shared/.env.local

and be symlinked into each release.

# Process model

The Node process is not started directly by Nginx and does not rely on a login shell.

Instead:

  1. systemd starts node-site-runner
  2. node-site-runner loads /etc/node-sites/<domain>.env
  3. it chooses the correct release directory
  4. it loads shared env files
  5. it uses fnm to run the requested Node version
  6. it starts the app command (default npm start)

This keeps the runtime deterministic and suitable for reboot persistence.

# Why fnm was chosen

  • simple per-user multi-version Node management
  • works for build and runtime
  • lightweight
  • easy to export to another VPS

Installed for the git user at:

  • /home/git/.local/share/fnm

# Security model

# Runtime users/groups

  • app owner: git
  • deploy user: cicd
  • web-readable group: webread
  • Nginx is a member of webread

# Restricted CI/CD sudo

cicd does not get blanket root.

It is limited to specific deployment commands and service operations described in the CI/CD doc.

# Validation already performed on this VPS

The reference sites have been verified for:

  • systemd startup
  • fnm Node20 and Node22 runtime
  • release switching
  • shared env loading
  • Nginx proxying
  • HTTPS for node.v4.sohophp.app
  • HTTP local host-header probing for node20.v4.sohophp.app
  • healthchecks
  • cicd-triggered deployment
  • one-command starter-site bootstrap