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" }. Some Prisma tooling also expects Node ≥ 22.
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
- Install Node.js 22 LTS (same major for shell and pm2 — pm2 uses the
nodebinary it was started with).
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 22
nvm alias default 22
node -v # v22.x
Option B — NodeSource / distro packages — use your distribution’s Node 22 packages so /usr/bin/node is v22.
-
Reinstall globals if needed (
npm i -g pm2), thenpm2 restart/pm2 reloadthe app from the same environment wherenode -vis 22. -
Optional: enforce engines in CI with
engine-strict=truein.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: documentation/site/— recreated bymkdocs buildon each deploy.- Prefer ownership of the clone by
coachingso builds never requiresudofor writing HTML.
What each step needs
| Area | Requirement |
|---|---|
| Git / npm / Node / Prisma / Next | Commands on PATH; no extra Unix privilege. |
MkDocs (documentation/build-from-deploy.sh) |
python3, python3 -m pip, write permission under documentation/site/. Optional write access for pip installs (see next section). |
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 / pip and --break-system-packages
The deploy script runs:
python3 -m pip install -r documentation/requirements.txt --break-system-packages -q
mkdocs build -q
On recent Debian / Ubuntu, system Python is marked externally managed (PEP 668), so pip install without a venv fails unless you use --break-system-packages or a virtual environment.
Using the flag lets the coaching user install MkDocs, mkdocs-material, and mkdocs-static-i18n into that Python environment without sudo. Trade-off: those packages live in the shared Python install used by python3 (often acceptable on a dedicated app server).
Isolation alternative
For stricter isolation, create documentation/.venv once (as coaching), pip install -r requirements.txt inside it, and change documentation/build-from-deploy.sh to source .venv/bin/activate before mkdocs build, without --break-system-packages.
Summary
| Privilege | Needed for |
|---|---|
Normal user (coaching) owning the repo |
Git pull, npm, Next build, MkDocs output, pm2 reload |
pip install … --break-system-packages |
MkDocs dependencies on PEP 668 hosts without a venv |
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 documentation/site/.