Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Docker ignore rules for flowfi
# Exclude files that inflate image context size but aren't needed at build time.

# Version control
.git
.gitignore
.gitattributes

# Dependency directories (re-installed inside the container)
node_modules
**/node_modules

# Build outputs (rebuilt inside the container)
dist
build
.next
out

# Test artifacts
coverage
*.test.*
*.spec.*
__tests__
__mocks__
test
tests
e2e

# Documentation
*.md
docs

# Environment / secrets
.env
.env.*
!.env.example

# Editor / IDE config
.vscode
.idea
*.swp
*.swo
.DS_Store

# CI
.github

# Logs
*.log
logs
npm-debug.log*

# Misc
*.tmp
*.bak
25 changes: 23 additions & 2 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# ============================================================
# Stage 1: builder — full Node toolchain to compile TypeScript
# ============================================================
FROM node:20-alpine@sha256:fb4cd12c85ee03686f6af5362a0b0d56d50c58a04632e6c0fb8363f609372293 AS builder

WORKDIR /app
Expand All @@ -11,16 +14,24 @@ WORKDIR /app
COPY package*.json ./
COPY prisma ./prisma

RUN npm install
# Install all deps (incl. devDependencies needed for tsc / prisma generate)
RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi

COPY tsconfig.json ./
COPY prisma.config.ts ./
COPY src ./src

RUN npm run build

# ============================================================
# Stage 2: production runner — minimal Alpine image, non-root
# ============================================================
FROM node:20-alpine@sha256:fb4cd12c85ee03686f6af5362a0b0d56d50c58a04632e6c0fb8363f609372293 AS runner

# Security: drop to non-root user for the running process
RUN addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 --ingroup nodejs nodeapp

WORKDIR /app

ENV NODE_ENV=production
Expand All @@ -34,7 +45,7 @@ ENV NODE_ENV=production
# `npx prisma db push` step the workflow runs against this container,
# while keeping the install layer cached whenever prisma/ is unchanged.
COPY package*.json ./
RUN npm install --omit=dev --ignore-scripts
RUN if [ -f package-lock.json ]; then npm ci --omit=dev --ignore-scripts; else npm install --omit=dev --ignore-scripts; fi

# Copy the prisma schema into the runner. The boot-and-check-health step
# in .github/workflows/ci.yml runs `npx prisma db push` against this
Expand All @@ -47,6 +58,16 @@ COPY --from=builder /app/src/generated ./dist/generated
COPY --from=builder /app/prisma ./prisma
COPY prisma.config.ts ./

# Ownership hand-off — ensure the non-root user owns all app files
RUN chown -R nodeapp:nodejs /app

USER nodeapp

EXPOSE 3001

# Health check so orchestrators (Docker Compose / Kubernetes) can detect
# when the API is ready to serve traffic.
HEALTHCHECK --interval=10s --timeout=5s --start-period=15s --retries=5 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1

CMD ["npm", "start"]
61 changes: 61 additions & 0 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# ============================================================
# Stage 1: deps — install production-only dependencies
# ============================================================
FROM node:20-alpine@sha256:fb4cd12c85ee03686f6af5362a0b0d56d50c58a04632e6c0fb8363f609372293 AS deps

WORKDIR /app

# Copy lockfile first for optimal Docker layer caching:
# rebuilds only when package files change, not source files.
COPY package*.json ./
RUN if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi

# ============================================================
# Stage 2: builder — compile Next.js application
# ============================================================
FROM node:20-alpine@sha256:fb4cd12c85ee03686f6af5362a0b0d56d50c58a04632e6c0fb8363f609372293 AS builder

WORKDIR /app

COPY package*.json ./
RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi

# Copy source files
COPY . .

# Build the Next.js application (output: .next directory)
RUN npm run build

# ============================================================
# Stage 3: production runner — minimal Alpine image, non-root
# ============================================================
FROM node:20-alpine@sha256:fb4cd12c85ee03686f6af5362a0b0d56d50c58a04632e6c0fb8363f609372293 AS runner

# Security: run as non-root user
RUN addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 --ingroup nodejs nextapp

WORKDIR /app

ENV NODE_ENV=production \
NEXT_TELEMETRY_DISABLED=1

# Copy only what is needed to run the app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/next.config.ts ./

# Ownership hand-off — ensure the non-root user owns all app files
RUN chown -R nextapp:nodejs /app

USER nextapp

EXPOSE 3000

# Health check so orchestrators know when Next.js is ready to serve
HEALTHCHECK --interval=15s --timeout=5s --start-period=30s --retries=5 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1

CMD ["npm", "start"]
Loading