# Deploying to a cPanel server

This app is a Next.js server (not a static site) with a local SQLite
database, so it needs cPanel's **"Setup Node.js App"** feature (built on
Phusion Passenger), not just static file hosting. Most cPanel hosts running
CloudLinux support this; check under **Software → Setup Node.js App** in
your cPanel dashboard.

First-time setup (creating the database and the first admin account) is
handled by an in-browser wizard - the first time you visit the site, you're
automatically taken to it. No terminal/SSH commands are required for that
part; see "First run" below. SSH is only needed for the initial upload (or
not, if you deploy via Git/SFTP/File Manager instead) and, later, for
applying a *new* migration when you update the app.

## Before you start: the one real risk

`better-sqlite3` (the database driver) is a **native C++ addon** - it has to
compile during `npm install`, which needs a C/C++ toolchain (`gcc`/`make`)
on whatever machine runs that install. Most cPanel/CloudLinux Node.js
Selector environments include this, but some locked-down shared hosts
don't.

**Test this first**, before following the rest of this guide: SSH into
your server (or use cPanel's Terminal), create a Node.js app via the Setup
Node.js App page, `cd` into its application root, and run:

```bash
npm install better-sqlite3
```

- **If it succeeds:** you're clear, continue below.
- **If it fails** with errors mentioning `gyp`, `make`, or a missing
  compiler: your host doesn't support compiling native modules. Options, in
  order of preference: (1) ask your host to enable a build toolchain for
  your account, (2) switch the database to something with prebuilt
  binaries or no native dependency (e.g. `@libsql/client`), or (3) if MySQL
  is available on your plan (it almost always is on cPanel), migrate to
  Prisma's MySQL provider instead of SQLite. Options 2 and 3 are real
  architecture changes, not a quick config edit - come back for help with
  those specifically if you hit this wall.

## 1. One-time server setup

In **Setup Node.js App**, create an application:

- **Node.js version:** 20.x or newer (check with `node -v` if unsure what's
  available)
- **Application mode:** Production
- **Application root:** a folder for the app, e.g. `horse-records-app`
  (cPanel creates this under your home directory)
- **Application URL:** the domain/subdomain you want this on
- **Application startup file:** `.next/standalone/server.js` (relative to
  the Application Root - explained in step 2)

Don't click "Start App" yet - the startup file doesn't exist until you've
built and uploaded the app (next section).

## 2. Upload the source and build on the server

**Build on the server, not on your own machine** (unless your dev machine
runs the same OS/CPU architecture as the server, which is unlikely if
you're on Windows or Mac and the server is Linux). `better-sqlite3` is a
native binary - one compiled on your laptop won't run on the server, so
`npm install` has to happen there, which is also where the build should
happen.

Upload the **source only** to the Application Root you created in step 1 -
`src/`, `prisma/`, `public/`, `scripts/`, and the root config files
(`package.json`, `package-lock.json`, `next.config.ts`, `prisma.config.ts`,
`tsconfig.json`, `postcss.config.mjs`). Leave out `node_modules/`, `.next/`,
your local `dev.db`/`backups/`, and your local `.env` - none of those
belong on the server, and a stray `.setup-complete` file in particular
would trick a fresh server into thinking it's already configured when it
isn't. Upload via Git (`git clone` the repo into the Application Root, if
you have SSH access and a remote set up), SFTP, or a zip through cPanel's
File Manager.

Then, via SSH or cPanel's Terminal, from the Application Root:

```bash
npm install
npx prisma generate
npm run build
```

This is the same `npm install` as the native-compile check in "before you
start" - if you already ran that check in this exact folder, you can skip
straight to `prisma generate`/`build`. `npm run build` also runs
`scripts/prepare-standalone.mjs` automatically (via the `postbuild`
script), which copies `public/` and `.next/static/` into
`.next/standalone/` so it's ready to run as-is.

## 3. Start the app

Back in Setup Node.js App, confirm the startup file is
`.next/standalone/server.js`, then click **Start App**. Passenger sets
`PORT` and proxies your domain to it automatically - the standalone server
already listens on `process.env.PORT`, no extra config needed, and no
environment variables are required to get this far (SQLite defaults to a
database file inside the Application Root if you don't set `DATABASE_URL`
- fine to start with, though the wizard in the next step will offer to
move it somewhere more durable).

## 4. First run: finish setup in your browser

Visit your domain. With no admin account yet, you're redirected to
`/setup` automatically - this is the entire remaining install:

1. Enter your name, email, and a password for the first (Admin) account.
   Your timezone is pre-filled from your browser; change it if it's wrong.
2. Optionally expand **Advanced** to move the database file and backup
   folder somewhere outside the Application Root (recommended, so they
   survive a redeploy - see "Updating the app later" below). Leave both
   blank to keep the defaults from step 3.
3. Click **Install**. This runs the pending database migrations, seeds the
   color/health genetics reference data, and creates your admin account -
   all server-side, no terminal needed. If you changed the database/backup
   location, the app also rewrites its `.env` and restarts itself
   automatically (Passenger picks this up via a `tmp/restart.txt` marker);
   you'll see a brief "Finishing setup…" screen while that happens, then
   land on the login page.

The wizard is permanently inaccessible once an admin account exists -
visiting `/setup` again just redirects to `/login`, whether or not you're
signed in.

**If your host doesn't run under Passenger** (so the auto-restart in step
2 has nothing to trigger it) and you changed the database/backup location:
restart the app manually from Setup Node.js App after the wizard finishes.

## Updating the app later

1. Upload your changed source files (same set as step 2 above - `src/`,
   `prisma/`, `public/`, `scripts/`, and any changed config files) over top
   of what's already in the Application Root.
2. From the Application Root, via SSH/cPanel Terminal: `npm install && npx
   prisma generate && npm run build` - same reasoning as the first deploy,
   this has to run on the server, not be built locally and uploaded.
3. If the update includes a new migration:
   ```bash
   npx prisma migrate deploy
   ```
   (The setup wizard only ever runs this once, for the very first install -
   this is the same command it ran for you then, now run by hand for a
   later schema change.)
4. Restart the app from Setup Node.js App (or `touch tmp/restart.txt` in
   the Application Root, if your Passenger setup watches for that).

If you moved the database/backup folder outside the Application Root
during setup, none of this touches it - your data is untouched by any
redeploy.

## Troubleshooting

- **`npm install`/`npm ci` prints `npm warn allow-scripts ... packages have
  install scripts not yet covered by allowScripts`:** newer npm blocks
  native/build install scripts (for `better-sqlite3`, `@prisma/engines`,
  `esbuild`, `prisma`, `unrs-resolver`) until explicitly approved, and
  skipping them silently leaves those packages without their real binaries.
  Approve them once per fresh `node_modules`, then reinstall so the scripts
  actually run:
  ```bash
  npm approve-scripts @prisma/engines better-sqlite3 esbuild prisma unrs-resolver
  rm -rf node_modules && npm ci
  ```
- **`npm run build` crashes with `spawn ... EAGAIN` or a Rust
  `ThreadPoolBuildError`/`WouldBlock` panic:** shared/CloudLinux hosting
  enforces a low per-account process/thread limit, and Next's build tries
  to use more parallelism than that allows. `next.config.ts` already caps
  this to a single build worker (`experimental.cpus: 1`,
  `workerThreads: false`); if it still happens, also cap Rust's own thread
  pool: `RAYON_NUM_THREADS=2 npm run build`.
- **App won't start / 503 from Passenger:** check the app's log in Setup
  Node.js App (or `~/logs/<app-name>/passenger.log` if accessible). The
  `better-sqlite3` compile issue above is the most common cause.
- **Stuck on `/setup` even after installing:** the wizard checks for a
  `.setup-complete` marker next to the database file, then falls back to
  checking the database directly. If you moved the database via the
  Advanced section and the automatic restart didn't happen (e.g. non-
  Passenger host), restart the app manually and reload.
- **"Cannot open database because the directory does not exist":** the
  wizard creates its target directory automatically; this would only
  happen if you're running `prisma migrate deploy` by hand (step 3 of
  "Updating the app later") against a `DATABASE_URL` whose folder doesn't
  exist yet - `mkdir -p` it first.
- **Backups aren't showing up / disappear after a redeploy:** confirm
  `BACKUP_DIR` (Setup Node.js App's environment variables, or set via the
  wizard's Advanced section) is an absolute path outside the Application
  Root, not left at its relative-path default.
