# Deploying Mega Plant to Coolify ## The one thing that catches everyone `NEXT_PUBLIC_PB_URL` must be set as a **Build Argument**, not only as an environment variable. Next.js compiles `NEXT_PUBLIC_*` values into the JavaScript the browser downloads. That happens at `npm run build`, inside the Docker build. Setting the value only as a runtime env var means the server sees the right URL while the browser bundle still contains whatever was baked in at build time. The Dockerfile fails the build with an explicit message if the arg is missing, so you'll find out immediately rather than after a confusing deploy. ## Setup **1. Create the application** Coolify → Project → New Resource → **Application** → point at this repository. Build Pack: **Dockerfile**. Port: **3000**. **2. Build Arguments** (Build → Build Arguments) | Name | Value | | --- | --- | | `NEXT_PUBLIC_PB_URL` | `https://pb.mega.plant.example` | **3. Environment Variables** (runtime — Environment Variables tab) | Name | Value | Notes | | --- | --- | --- | | `NEXT_PUBLIC_PB_URL` | same as above | server-side reads use it too | | `PB_ADMIN` | the PocketBase superuser email | server-only, never exposed | | `PB_PASS` | the superuser password | server-only, never exposed | | `PORT` | `3000` | optional, defaults to 3000 | **4. Health check** Coolify → Health Checks → path `/api/health`, port `3000`. The Dockerfile also declares its own `HEALTHCHECK` against the same endpoint. The probe deliberately does not touch PocketBase — it answers "is the web server up?". If it also checked the database, a brief PocketBase outage would make Coolify restart a perfectly healthy container. **5. Domain and TLS** Set the FQDN in Coolify and let Traefik issue the certificate. **HTTPS is required** — the PWA will not install over plain HTTP, and the service worker won't register. ## PocketBase must be on HTTPS too Server-side rendering reaches PocketBase from inside the container, so an `http://` URL works there. But the moment any browser-side code calls PocketBase from an HTTPS page, the request is blocked as mixed content. Put PocketBase behind TLS. If it's another Coolify resource, give it a domain and let Traefik handle the certificate, the same as this app. ## Before the first deploy - [ ] **Rotate the PocketBase superuser password.** The old one (`plant@megainfra.co.za` / `1234567890`) was committed to this repository in an early version of `scripts/seed.mjs`. Treat it as compromised. - [ ] Confirm `.env.local` is not committed (it's in `.gitignore`). - [ ] Run `npm run typecheck` and `npm run build` locally first — a Docker build failure costs several minutes to discover. ## Test the image locally ```bash docker build --build-arg NEXT_PUBLIC_PB_URL=https://pb.mega.plant.example -t mega-plant . ``` ```bash docker run --rm -p 3000:3000 -e PB_ADMIN=you@example.com -e PB_PASS=secret mega-plant ``` Then check the health endpoint: ```bash curl -s localhost:3000/api/health ``` ## Notes - **`--webpack` is not optional.** `@ducanh2912/next-pwa` is Webpack-only and generates `public/sw.js`. `package.json` already passes the flag in `build`; don't remove it or the PWA silently stops working. - **`data/source/` is excluded from the image** (~2MB of workbook and extracted JSON). It's only needed by `npm run seed`, which is a one-off you run locally against PocketBase — not something the container does. - **Seeding is not part of deployment.** The container never writes to PocketBase on boot. Run `npm run seed` deliberately, from your machine. - The image runs as a non-root `nextjs` user on Node 20 Alpine.