When to Use
- •Building the
infra/folder (Dockerfiles, compose, env templates) - •Updating how frontend ↔ backend communicate in containerized environments
- •Adding scripts for local Docker workflows or CI pipelines tied to infra
- •Auditing existing Docker settings to keep them aligned with
docs/architecture.md
Critical Patterns
Folder Layout
code
infra/ ├── docker-compose.yml ├── frontend.Dockerfile ├── backend.Dockerfile └── README.md (explain ports, env, volume usage)
- •Keep infra-specific assets isolated from app packages
- •Compose file must live under
infra/and reference Dockerfiles via relative paths (e.g.,build: { context: .., dockerfile: infra/frontend.Dockerfile })
Service Contracts
| Service | Port (container → host) | Notes |
|---|---|---|
frontend | 3000:3000 | Runs next start; use env NEXT_PUBLIC_API_BASE_URL=http://backend:4000 or Compose network alias |
backend | 4000:4000 | Runs Express build output (node dist/app.js) |
- •No database or queue services for this challenge. Reject attempts to introduce them.
- •Use Docker network defaults so
frontendcan reachhttp://backend:4000internally; do not expose backend publicly unless required.
Dockerfile Expectations
Frontend:
- •Multi-stage build (
builder,runner) - •Base image:
node:20-alpine(matches engines) - •Copy monorepo files, install pnpm globally via corepack (or
pnpm fetch), thenpnpm --filter frontend... - •Output:
.next/,node_modulesfor production only (usenext build && next start) - •Set
PORT=3000,HOST=0.0.0.0
Backend:
- •Multi-stage (
buildercompiles TypeScript,runnercopiesdist/) - •Use
pnpm fetch+pnpm install --offlineto leverage the lockfile - •Expose
PORT=4000 - •Entrypoint:
node dist/app.js
Shared package should be built in the builder stage via pnpm --filter shared build before frontend/backend builds.
Compose Guardrails
- •Always mount
.env.example→.envwith explicitenv_file:entries; never rely on host envs implicitly - •Use
depends_onso backend is ready before frontend tries to proxy API calls - •Provide a
volumes:override only for dev (e.g.,.:/app+pnpm install), but default compose stack should run the built artifacts - •Document commands inside
infra/README.md
Networking & Rewrites
- •Next.js should continue to proxy
/apitobackend:4000; do not open CORS on backend unless Compose rewrites prove impossible - •For local Docker usage, prefer hitting
http://localhost:3000and let rewrites map to backend automatically
ALWAYS
- •Build shared artifacts in dependency order (shared → backend/frontend) inside Dockerfiles
- •Copy only necessary files into runtime layers (no
.git, no local dev config) - •Keep Compose deterministic: no
latesttags, always build from the local source - •Mirror the same Node/pnpm versions defined in
package.jsonengines
NEVER
- •Add databases, queues, or extra services unless the spec explicitly changes
- •Run
pnpm installtwice (usepnpm fetch+pnpm install --offlineonce per stage) - •Use root user in production layers; switch to
nodeuser when possible - •Bake secrets or
.envvalues directly into images—pass through env vars at runtime
Commands
bash
# Build & run the stack locally (from repo root) # Tear down containers and remove orphaned services # Rebuild a single service docker compose -f infra/docker-compose.yml build backend # Follow logs # Clean dangling images created during local iterations