Skip to content

Deploy pipeline — coaching user permissions

The admin Deploy page runs shell steps as the same Unix user as the Next.js / pm2 process (typically coaching). Below is what that user must be allowed to do.

Node.js version (npm warn EBADENGINE)

package.json declares "engines": { "node": ">=22.0.0", "npm": ">=10.0.0" }. The repository pins Node 24 in .nvmrc (production / team default). Some Prisma and test-tooling packages advertise ^22.12 || >=24 — Node 24 satisfies everything cleanly.

If production runs Node 20 (or older), npm install during Deploy prints EBADENGINE Unsupported engine — npm still installs by default, but you are outside the supported range and should upgrade.

Fix on the server

  1. Install Node.js 24 (same major for shell and pm2 — pm2 uses the node binary it was started with). Minimum supported by this project is 22; do not stay on 20.

Option A — nvm (as coaching or deploy user):

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.nvm/nvm.sh
nvm install 24
nvm alias default 24
node -v   # v24.x

Option B — NodeSource / distro packages — use your distribution’s Node 24 (or at least 22) packages so /usr/bin/node meets engines.

  1. Reinstall globals if needed (npm i -g pm2), then pm2 restart / pm2 reload the app from the same environment where node -v matches production.

  2. Optional: enforce engines in CI with engine-strict=true in .npmrc — not recommended on the server until Node is upgraded.

Do not “fix” only package.json engines

Lowering engines to Node 20 silences the warning for coach-studio, but transitive packages (e.g. @prisma/streams-local) can still report EBADENGINE until Node is actually ≥ 22.

If PM2 runs under systemd, put the Node bin directory first in the unit PATH — see PM2 systemd and Node.js.

Repository and generated files

  • Read/write on the whole clone (e.g. /var/www/coaching), including:
  • apps/docs/site/ — recreated by mkdocs build on each deploy.
  • Prefer ownership of the clone by coaching so builds never require sudo for writing HTML.
  • If an old sudo npm left root-owned files (typical EACCES unlink sharp), deploy runs deploy/fix-ownership-from-deploy.sh before npm ci.

Fix ownership automatically on deploy

  1. Once on the server (as root):
sudo bash /var/www/coaching/deploy/fix-ownership.sh
  1. Optional — allow the Deploy step without a password:
sudo visudo -f /etc/sudoers.d/coaching-ownership
coaching ALL=(root) NOPASSWD: /bin/bash /var/www/coaching/deploy/fix-ownership.sh

Without that line, if root-owned files exist the step fails with instructions (instead of failing later in npm with EACCES).

What each step needs

Area Requirement
Git / npm / Node / Prisma / Next Commands on PATH; no extra Unix privilege.
Ownership (deploy/fix-ownership-from-deploy.sh) sudo only if you configured passwordless fix-ownership.sh (above). Otherwise the clone must already be owned by coaching.
MkDocs (apps/docs/build-from-deploy.sh) python3 with venv, write permission under apps/docs/.venv/ and apps/docs/site/.
Systemd cron (deploy/cron/install-from-deploy.sh) sudo only if you configured passwordless install.sh — see deploy/cron/README.md. Otherwise the step prints SKIP and exits successfully.

Python / MkDocs virtualenv

The deploy script now keeps MkDocs isolated inside apps/docs/.venv:

cd /var/www/coaching
bash apps/docs/build-from-deploy.sh

It creates the venv if missing, installs apps/docs/requirements.txt into that venv, and runs mkdocs build from there. This avoids Debian / Ubuntu PEP 668 failures and does not install Python packages into the system environment.

If the server package set lacks venv support, install it once, for example sudo apt install python3-venv.

Summary

Privilege Needed for
Normal user (coaching) owning the repo Git pull, npm, Next build, MkDocs output, pm2 reload
Write access to apps/docs/.venv/ and apps/docs/site/ MkDocs dependencies and generated static docs
Passwordless sudo for deploy/fix-ownership.sh only Optional — fix root-owned files during deploy
Passwordless sudo for deploy/cron/install.sh only Optional — refresh systemd timer units during deploy

Root (sudo) is not required for the documentation build if coaching can write apps/docs/site/.