Skip to content
Merged
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
104 changes: 104 additions & 0 deletions apps/api/Dockerfile.multistage
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# =============================================================================
# STAGE 1: Dependencies - Install workspace dependencies
# =============================================================================
FROM oven/bun:1.2.8 AS deps

WORKDIR /app

# Copy root workspace config
COPY package.json bun.lock ./

# Copy all workspace package.json files
COPY packages/db/package.json ./packages/db/
COPY packages/utils/package.json ./packages/utils/
COPY packages/integration-platform/package.json ./packages/integration-platform/
COPY packages/tsconfig/package.json ./packages/tsconfig/

# Copy API package.json
COPY apps/api/package.json ./apps/api/

# Install all dependencies (including workspace deps)
RUN bun install

# =============================================================================
# STAGE 2: Builder - Build workspace packages and NestJS app
# =============================================================================
FROM deps AS builder

WORKDIR /app

# Copy workspace packages source
COPY packages/db ./packages/db
COPY packages/utils ./packages/utils
COPY packages/integration-platform ./packages/integration-platform
COPY packages/tsconfig ./packages/tsconfig

# Copy API source
COPY apps/api ./apps/api

# Bring in node_modules from deps stage
COPY --from=deps /app/node_modules ./node_modules

# Build workspace packages
RUN cd packages/db && bun run build && cd ../..
RUN cd packages/integration-platform && bun run build && cd ../..

# Generate Prisma client for API (copy schema and generate)
RUN cd packages/db && node scripts/combine-schemas.js && cd ../..
RUN cp packages/db/dist/schema.prisma apps/api/prisma/schema.prisma
RUN cd apps/api && bunx prisma generate

# Build NestJS application (skip prebuild since we already generated Prisma)
RUN cd apps/api && bunx nest build

# =============================================================================
# STAGE 3: Production Runtime
# =============================================================================
FROM node:20-alpine AS production

WORKDIR /app

# Install runtime dependencies
RUN apk add --no-cache wget libc6-compat openssl

# Copy built NestJS app
COPY --from=builder /app/apps/api/dist ./dist

# Copy prisma files
COPY --from=builder /app/apps/api/prisma ./prisma

# Copy package.json (for any runtime needs)
COPY --from=builder /app/apps/api/package.json ./package.json

# Copy workspace packages that are referenced by node_modules symlinks
COPY --from=builder /app/packages/db ./packages/db
COPY --from=builder /app/packages/utils ./packages/utils
COPY --from=builder /app/packages/integration-platform ./packages/integration-platform
COPY --from=builder /app/packages/tsconfig ./packages/tsconfig

# Copy production node_modules (includes symlinks to workspace packages above)
COPY --from=builder /app/node_modules ./node_modules

# Set production environment
ENV NODE_ENV=production
ENV PORT=3333

# Install Prisma CLI and regenerate client in production stage
RUN npm install -g prisma@6.13.0 && \
prisma generate --schema=./prisma/schema.prisma
Comment on lines +80 to +88
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical: Runtime incompatibility between Bun and Node.js dependencies

The node_modules directory is installed with Bun (stage 1, line 21) but copied to a Node.js runtime (stage 3, line 80). While Prisma is regenerated (lines 87-88), other packages with native bindings may fail at runtime because they were compiled for Bun's runtime, not Node.js.

Impact: Production crashes when loading native modules (e.g., bcrypt, sharp, any C++ addons)

Fix: Either use the same runtime in all stages, or reinstall production dependencies in the Node.js stage:

# Option 1: Use Node.js throughout
FROM node:20-alpine AS deps
RUN npm install

# Option 2: Reinstall in production stage (after line 71)
RUN npm ci --omit=dev
RUN npm install -g prisma@6.13.0 && prisma generate
Suggested change
COPY --from=builder /app/node_modules ./node_modules
# Set production environment
ENV NODE_ENV=production
ENV PORT=3333
# Install Prisma CLI and regenerate client in production stage
RUN npm install -g prisma@6.13.0 && \
prisma generate --schema=./prisma/schema.prisma
COPY package*.json ./
# Set production environment
ENV NODE_ENV=production
ENV PORT=3333
# Install production dependencies and Prisma CLI
RUN npm ci --omit=dev
RUN npm install -g prisma@6.13.0 && \
prisma generate --schema=./prisma/schema.prisma

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.


# Create non-root user
RUN addgroup --system nestjs && adduser --system --ingroup nestjs nestjs \
&& chown -R nestjs:nestjs /app

USER nestjs

EXPOSE 3333

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3333/v1/health || exit 1

# Start the application
CMD ["node", "dist/src/main.js"]

38 changes: 38 additions & 0 deletions apps/api/buildspec.multistage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
version: 0.2

# Simplified buildspec that uses multi stage Docker build
# al building happens inside Docker - CodeBuild just orchestrates ECR/ECS

phases:
pre_build:
commands:
- echo "Logging in to Amazon ECR..."
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- IMAGE_TAG=${COMMIT_HASH:=latest}

build:
commands:
- echo "Building Docker image with multi-stage build..."
- cd apps/api
- docker build --build-arg BUILDKIT_INLINE_CACHE=1 --target production -f Dockerfile.multistage -t $ECR_REPOSITORY_URI:$IMAGE_TAG ../..
- docker tag $ECR_REPOSITORY_URI:$IMAGE_TAG $ECR_REPOSITORY_URI:latest

post_build:
commands:
- echo "Pushing images to ECR..."
- docker push $ECR_REPOSITORY_URI:$IMAGE_TAG
- docker push $ECR_REPOSITORY_URI:latest
- echo "Updating ECS service..."
- aws ecs update-service --cluster $ECS_CLUSTER_NAME --service $ECS_SERVICE_NAME --force-new-deployment
- 'printf "[{\"name\":\"%s-container\",\"imageUri\":\"%s\"}]" api $ECR_REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json'

cache:
paths:
- '/root/.docker/buildx/cache/**/*'

artifacts:
files:
- imagedefinitions.json
name: ${APP_NAME}-build

28 changes: 28 additions & 0 deletions apps/api/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# FOR TESTING LOCALY ONLY
version: '3.8'

services:
api:
build:
context: ../..
dockerfile: apps/api/Dockerfile.multistage
target: production
container_name: comp-api-test
ports:
- "3333:3333"
environment:
- NODE_ENV=production
- PORT=3333
env_file:
- .env

healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3333/v1/health"]
interval: 30s
timeout: 3s
retries: 3
start_period: 5s

restart: unless-stopped


1 change: 1 addition & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"class-transformer": "^0.5.1",
"class-validator": "^0.14.2",
"dotenv": "^17.2.3",
"esbuild": "^0.27.1",
"exceljs": "^4.4.0",
"express": "^4.21.2",
"helmet": "^8.1.0",
Expand Down
Loading
Loading