docker: multi-stage Dockerfile for adapter-node runtime #10
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "7-dockerfile-multistage"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Adds a multi-stage Dockerfile + .dockerignore for westside-admin. Builder stage compiles SvelteKit (npm ci + npm run build); runtime stage copies only
build/plus production deps and runs as non-rootnode(UID 1000) on port 3000.Changes
Dockerfile— two-stagenode:22-alpinebuild,npm cifor reproducibility (lockfile committed in PR #9),NODE_ENV=production, drops tonodeuser,EXPOSE 3000,CMD ["node", "build/index.js"]. No HEALTHCHECK directive — k8s probes (kustomize overlay PR #134) own/health..dockerignore— excludes node_modules, build, .svelte-kit, .git, .env*, *.md, .playwright-mcp, .claude, screenshots, etc. Includes thenode_modules/.vite-tempexclusion that fixed prior Kaniko snapshot failures in westside-app.Test Plan
Verified locally:
docker build -t westside-admin:test .succeedsdocker run -p 3000:3000 westside-admin:test-> "Listening on http://0.0.0.0:3000"curl http://localhost:3000/health-> HTTP 200docker run --rm westside-admin:test id->uid=1000(node) gid=1000(node)(non-root)Review Checklist
docker buildsucceeds locally/healthreturns 200 (k8s probes own HEALTHCHECK semantics)npm ci+ committed lockfileRelated Notes
story-westside-admin-admin-row-crudarch-deployment-westside-admin(Harbor project from #65)~/westside-app/Dockerfile(adapter-static + nginx — different runtime, similar dockerignore pattern)PR #10 Review
DOMAIN REVIEW
Stack: Dockerfile (multi-stage) + .dockerignore for SvelteKit adapter-node on
node:22-alpine. Containerization / k8s deployment domain.Multi-stage soundness: Clean separation. Builder stage runs
npm cithennpm run build. Runtime stage starts from a freshnode:22-alpine, installs prod-only deps withnpm ci --omit=dev, copiesbuild/from builder, and runsnode build/index.js. No dev deps, no source files, no.svelte-kit/cache leak into runtime. Matches the adapter-nodeout: 'build'convention.Reproducibility:
npm ciin BOTH stages (builder + runtime). Lockfile from PR #9 honored. Nonpm installanywhere. Matches Track A QA forward-flag.Non-root:
chown -R node:node /appthenUSER node.node:alpineships UID 1000nodeuser. Dev's localidconfirmsuid=1000(node) gid=1000(node). Correct.Image surface / size: 240MB reported (under 250MB target). Layering is minimal: prod deps + build output + package.json on alpine base.
npm cache clean --forceafter the runtime install keeps the layer lean. No Tailwind/PostCSS detected (compliant withfeedback_no_tailwind).Healthcheck delegation: No
HEALTHCHECKdirective. Comment explicitly hands liveness/readiness to k8s probes (kustomize overlay #134). Correct per platform convention — Dockerfile shouldn't duplicate k8s probe semantics.Base pin:
node:22-alpine(specific major). Fine — a stronger pin thannode:lts-alpine, and matchesbasketball-apiandwestside-appsiblings. No drift risk fromltsfloating tag.Secrets: No
ARG/ENVcarrying sensitive values..dockerignoreexcludes.envand.env.*. No COPY of secret files. Clean..dockerignore: Excludes
node_modules,build,.svelte-kit,.git,.env*,*.md,*.png,.playwright-mcp,.claude, coverage/test-results. Includes thenode_modules/.vite-tempexclusion that previously broke Kaniko in westside-app — good carry-forward.Sibling comparison: westside-app uses adapter-static + nginx (different runtime). Dev correctly noted the structural divergence in PR body. basketball-api shows the multi-stage shape this PR follows. No copy-paste mismatch.
BLOCKERS
None.
NITS
COPY package.json package-lock.json ./in the runtime stage couldCOPY --from=builder /app/package*.json ./to save one copy operation, but doing it from the host is also fine and arguably more transparent. Non-blocking.COPY . .which will pull every non-ignored file into the build context layer. Acceptable since the runtime stage is fresh, but if future scaffolding adds large fixtures, scope.dockerignoreagain.RUN chown -R node:node /appcould be avoided by usingCOPY --chown=node:nodeon each prior COPY, saving a layer. Cosmetic.SOP COMPLIANCE
7-dockerfile-multistage)story-westside-admin-admin-row-crud,arch-deployment-westside-admin); repo has no labels yet — Ava is filing that separately, acknowledged.woodpecker.yaml, no scaffold drift (sibling PR #11 covers CI)docker build,docker run,curl /health,idnon-root check, image sizePROCESS OBSERVATIONS
.woodpecker.yamllands in the sibling PR (#11 / issue #8). That's correct sequencing — Dockerfile must exist before pipeline can build it. Validation gate after merge should confirm Kaniko-in-CI matches the localdocker buildoutcome.node:22-alpinewith full prod deps is healthy. If size pressure ever rises, considernode:22-alpine+--productioninstall in a separatedepsstage and copynode_modulesacross to skip the secondnpm cinetwork round-trip.VERDICT: APPROVED