# ─────────────────────────────────────────────────── # Coastal Timesheet — Backend Dockerfile # Node 22 Alpine · Prisma · Express # ─────────────────────────────────────────────────── # ── Stage 1: Install dependencies ────────────────── FROM node:22-alpine AS deps WORKDIR /app # Copy package files COPY backend/package.json backend/package-lock.json* ./ # Install production dependencies RUN npm ci --omit=dev 2>/dev/null || npm install --omit=dev # ── Stage 2: Build (generate Prisma client) ──────── FROM node:22-alpine AS builder WORKDIR /app # Copy deps from previous stage COPY --from=deps /app/node_modules ./node_modules COPY backend/ ./ # Generate Prisma client RUN npx prisma generate # ── Stage 3: Production image ───────────────────── FROM node:22-alpine AS runner WORKDIR /app # Add non-root user for security RUN addgroup --system --gid 1001 coastal && \ adduser --system --uid 1001 coastal # Copy application COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/prisma ./prisma COPY --from=builder /app/src ./src COPY --from=builder /app/package.json ./ # Fix ownership so non-root user can regenerate Prisma client RUN chown -R coastal:coastal /app # Switch to non-root user USER coastal # Expose port EXPOSE 3001 # Health check HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD node -e "fetch('http://localhost:3001/api/health').then(r=>{if(!r.ok)throw 1}).catch(()=>process.exit(1))" # Start with migration and seed on first run CMD ["sh", "-c", "npx prisma db push 2>/dev/null; node prisma/seed.js 2>/dev/null; node src/index.js"]