mega-plant/COOLIFY.md
Mega Plant 0516220110 Round 4: regressions fixed + mobile standard
Part A — Round 3 regressions:
- A.1: verify-cost-reconciliation.mjs now loads .env.local, wraps in try/catch, exits 1 on any failure
- A.2: Vehicle Costs restored Santam — back to R 14,996.31 (matches row-by-row sum)
- A.3: incl<excl / VAT math / VAT>20% now FATAL — seed exits 2 with full report (4 anomalies on Charl-CCS + Buildsmart)
- A.4: Reconciliation target R 694,962.93 with both deltas itemised (Personnel +R19,800, Santam -R5,322.44, unexplained R 0.00)
- A.5: 'Lines' view uses actual lines (recharts LineChart), no fills; fills only in 'Cost stack'

Part B — Mobile standard:
- B.1: header wraps to 2 rows on mobile; YTD badge cards stack; page-wide overflow eliminated on /income, /charts, /overheads, /running, /expenses
- B.2: IncomeView card row layout stacked — name gets its own line on mobile, never truncated to single char
- B.3: Plant type filter is a <select> dropdown below lg (was 18 pills wrapping to 13 rows); same for sort on /income and /running
- B.4: Plant table first column sticky; -webkit-overflow-scrolling: touch; tables scroll inside their own container
- B.5: All buttons/links use min-h-[44px] tap targets; text below 12px is bumped where it matters
- B.6: Charts legend wraps below title; chart min-height h-44 → h-52 on mobile
- B.7: paddingTop env(safe-area-inset-top) on sticky header for notch/home indicator clearance

AGENTS.md created with the standing mobile checklist rule.
2026-09-12 12:05:43 +02:00

3.6 KiB

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

docker build --build-arg NEXT_PUBLIC_PB_URL=https://pb.mega.plant.example -t mega-plant .
docker run --rm -p 3000:3000 -e PB_ADMIN=you@example.com -e PB_PASS=secret mega-plant

Then check the health endpoint:

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.