You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The owner asked for Docker/devcontainer. The honest answer is not both — here is the split and the reasoning.
Scope:Dockerfile (new), .dockerignore (new), compose.yaml (new), heroku.yml (new), Procfile (delete, if the container stack replaces it), .github/workflows/ci-cd.yml, RUNBOOK.md
Depends on:#9, #15 — the compose.yaml half depends on nothing and may be pulled forward into any earlier wave
Checklist
Add compose.yaml with a single postgres:17 service, a named volume, a pinned port, and dev-only credentials. No app service — the app runs on the host via dev; the point is an offline database, not a full local stack
Document it in RUNBOOK.md as the local DATABASE_URL alternative to a Neon branch, and as the target for restoring the task-1 rescue dump
verify: docker compose up -d then prisma migrate deploy against the local URL succeeds, db:seed seeds TariffConfig, and dev boots against it with GET /api/health returning ok
Decision gate. Build the image only if at least one is true: task 18's Bun adoption is proceeding on Heroku (no official buildpack ⇒ container stack), or a Render cutover is actually happening. If neither holds, stop here, record the reason in the issue, and leave the compose half as the delivered value
Write a multi-stage Dockerfile: builder installs dev dependencies, runs prisma generate, and builds shared → backend → frontend; the runtime stage carries production dependencies, backend/dist, frontend/dist, the Prisma schema and migrations, and the generated client
Base on debian-slim, not alpine. Both sharp and Prisma's engines want glibc/OpenSSL; musl means chasing linux-musl-openssl-3.0.x binary targets and platform-specific sharp installs for no benefit on a solo project
If the image runs Bun, remember that the Prisma CLI's dynamic subcommand loading wants npm present — the oven/bun base image has no npm, while node:*-slim does. Whichever base is chosen, the release-phase migration must be proven inside the image, not just locally
Add .dockerignore covering node_modules, .git, docs/, assets/, frontend/public/*.webp, and dist — without it the build context balloons and layer caching is useless
Add heroku.yml with the build stage and a release: phase running the migration deploy (this is the container-stack equivalent of the current Procfile release line, and it is also the answer to Render free's missing pre-deploy hook noted in task 10)
Add a HEALTHCHECK against the existing /api/health route
Size for the constrained target: Render free is 512 MB RAM / 0.1 CPU with no persistent disk. Nothing in the image may assume local disk persistence for GeoTIFF caching — after task 6 that is R2's job. Record the final image size and the container's idle RSS in the issue
Build the image in CI on every PR (with layer caching) so it cannot silently rot. Do not push it from CI — building is the freshness guarantee, pushing is a separate decision
verify: docker build succeeds from a clean context, and the resulting container serves GET /api/health locally with only the documented env vars set
verify: inside the container, a cached project loads end-to-end and a panel drag recomputes flux with zero outbound calls to solar.googleapis.com — this proves sharp, geotiff.js, and the R2 client all work in the image, which is the only part of containerisation that can genuinely fail
verify: a staging deploy of the image (Heroku container stack or Render Docker) boots and passes the same checks, and the release-phase migration ran
verify: CI fails if the Dockerfile breaks — introduce a deliberate error and confirm the pipeline goes red, then revert. An image build that cannot fail the pipeline is not a guarantee
Priority: Low · Phase 12 task 21 of the maintainer’s migration plan.
The owner asked for Docker/devcontainer. The honest answer is not both — here is the split and the reasoning.
Scope:
Dockerfile(new),.dockerignore(new),compose.yaml(new),heroku.yml(new),Procfile(delete, if the container stack replaces it),.github/workflows/ci-cd.yml,RUNBOOK.mdDepends on: #9, #15 — the
compose.yamlhalf depends on nothing and may be pulled forward into any earlier waveChecklist
compose.yamlwith a singlepostgres:17service, a named volume, a pinned port, and dev-only credentials. No app service — the app runs on the host viadev; the point is an offline database, not a full local stackRUNBOOK.mdas the localDATABASE_URLalternative to a Neon branch, and as the target for restoring the task-1 rescue dumpdocker compose up -dthenprisma migrate deployagainst the local URL succeeds,db:seedseedsTariffConfig, anddevboots against it withGET /api/healthreturning okDockerfile: builder installs dev dependencies, runsprisma generate, and buildsshared→backend→frontend; the runtime stage carries production dependencies,backend/dist,frontend/dist, the Prisma schema and migrations, and the generated clientsharpand Prisma's engines want glibc/OpenSSL; musl means chasinglinux-musl-openssl-3.0.xbinary targets and platform-specificsharpinstalls for no benefit on a solo projectoven/bunbase image has no npm, whilenode:*-slimdoes. Whichever base is chosen, the release-phase migration must be proven inside the image, not just locally.dockerignorecoveringnode_modules,.git,docs/,assets/,frontend/public/*.webp, anddist— without it the build context balloons and layer caching is uselessheroku.ymlwith the build stage and arelease:phase running the migration deploy (this is the container-stack equivalent of the currentProcfilerelease line, and it is also the answer to Render free's missing pre-deploy hook noted in task 10)HEALTHCHECKagainst the existing/api/healthroutedocker buildsucceeds from a clean context, and the resulting container servesGET /api/healthlocally with only the documented env vars setsolar.googleapis.com— this provessharp,geotiff.js, and the R2 client all work in the image, which is the only part of containerisation that can genuinely failPriority: Low · Phase 12 task 21 of the maintainer’s migration plan.