node-pty requires a compiled native addon (.node binary) that may not be available in all Docker environments. The eager import crashed the entire API at startup. Changed to dynamic import() in onModuleInit() so the service degrades gracefully — terminal sessions are disabled but all other API functionality works. Also added explicit node-gyp rebuild to Dockerfile deps stage since pnpm may skip postinstall scripts for native addons. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
116 lines
4.6 KiB
Docker
116 lines
4.6 KiB
Docker
# Base image for all stages
|
|
# Uses Debian slim (glibc) instead of Alpine (musl) because native Node.js addons
|
|
# (matrix-sdk-crypto-nodejs, Prisma engines) require glibc-compatible binaries.
|
|
FROM node:24-slim AS base
|
|
|
|
# Install pnpm globally
|
|
RUN corepack enable && corepack prepare pnpm@10.27.0 --activate
|
|
|
|
# Set working directory
|
|
WORKDIR /app
|
|
|
|
# Copy monorepo configuration files
|
|
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
|
|
COPY turbo.json ./
|
|
|
|
# ======================
|
|
# Dependencies stage
|
|
# ======================
|
|
FROM base AS deps
|
|
|
|
# Install build tools for native addons (node-pty requires node-gyp compilation)
|
|
# and OpenSSL for Prisma engine detection
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
python3 make g++ openssl \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Copy all package.json files for workspace resolution
|
|
COPY packages/shared/package.json ./packages/shared/
|
|
COPY packages/ui/package.json ./packages/ui/
|
|
COPY packages/config/package.json ./packages/config/
|
|
COPY apps/api/package.json ./apps/api/
|
|
|
|
# Install dependencies (no cache mount — Kaniko builds are ephemeral in CI)
|
|
# Then explicitly rebuild node-pty from source since pnpm may skip postinstall
|
|
# scripts or fail to find prebuilt binaries for this Node.js version
|
|
RUN pnpm install --frozen-lockfile \
|
|
&& cd node_modules/.pnpm/node-pty@*/node_modules/node-pty \
|
|
&& npx node-gyp rebuild 2>&1 || true
|
|
|
|
# ======================
|
|
# Builder stage
|
|
# ======================
|
|
FROM base AS builder
|
|
|
|
# Copy root node_modules from deps
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
|
|
# Copy all source code FIRST
|
|
COPY packages ./packages
|
|
COPY apps/api ./apps/api
|
|
|
|
# Then copy workspace node_modules from deps (these go AFTER source to avoid being overwritten)
|
|
COPY --from=deps /app/packages/shared/node_modules ./packages/shared/node_modules
|
|
COPY --from=deps /app/packages/config/node_modules ./packages/config/node_modules
|
|
COPY --from=deps /app/apps/api/node_modules ./apps/api/node_modules
|
|
|
|
# Build the API app and its dependencies using TurboRepo
|
|
# --force disables turbo cache to ensure fresh build from source
|
|
RUN pnpm turbo build --filter=@mosaic/api --force
|
|
|
|
# ======================
|
|
# Production stage
|
|
# ======================
|
|
FROM node:24-slim AS production
|
|
|
|
# Install dumb-init for proper signal handling (static binary from GitHub,
|
|
# avoids apt-get which fails under Kaniko with bookworm GPG signature errors)
|
|
ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64 /usr/local/bin/dumb-init
|
|
|
|
# Single RUN to minimize Kaniko filesystem snapshots (each RUN = full snapshot)
|
|
# - openssl: Prisma engine detection requires libssl
|
|
# - No build tools needed here — native addons are compiled in the deps stage
|
|
RUN apt-get update && apt-get install -y --no-install-recommends openssl \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx \
|
|
&& chmod 755 /usr/local/bin/dumb-init \
|
|
&& groupadd -g 1001 nodejs && useradd -m -u 1001 -g nodejs nestjs
|
|
|
|
WORKDIR /app
|
|
|
|
# Copy node_modules from builder (includes generated Prisma client in pnpm store)
|
|
# pnpm stores the Prisma client in node_modules/.pnpm/.../.prisma, so we need the full tree
|
|
COPY --from=builder --chown=nestjs:nodejs /app/node_modules ./node_modules
|
|
|
|
# Copy built packages (includes dist/ directories)
|
|
COPY --from=builder --chown=nestjs:nodejs /app/packages ./packages
|
|
|
|
# Copy built API application
|
|
COPY --from=builder --chown=nestjs:nodejs /app/apps/api/dist ./apps/api/dist
|
|
COPY --from=builder --chown=nestjs:nodejs /app/apps/api/prisma ./apps/api/prisma
|
|
COPY --from=builder --chown=nestjs:nodejs /app/apps/api/package.json ./apps/api/
|
|
# Copy app's node_modules which contains symlinks to root node_modules
|
|
COPY --from=builder --chown=nestjs:nodejs /app/apps/api/node_modules ./apps/api/node_modules
|
|
|
|
# Copy entrypoint script (runs migrations before starting app)
|
|
COPY --from=builder --chown=nestjs:nodejs /app/apps/api/docker-entrypoint.sh ./apps/api/
|
|
|
|
# Set working directory to API app
|
|
WORKDIR /app/apps/api
|
|
|
|
# Switch to non-root user
|
|
USER nestjs
|
|
|
|
# Expose API port (default 3001, can be overridden via PORT env var)
|
|
EXPOSE ${PORT:-3001}
|
|
|
|
# Health check uses PORT env var (set by docker-compose or defaults to 3001)
|
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
|
CMD node -e "const port = process.env.PORT || 3001; require('http').get('http://localhost:' + port + '/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
|
|
|
# Use dumb-init to handle signals properly
|
|
ENTRYPOINT ["dumb-init", "--"]
|
|
|
|
# Run migrations then start the application
|
|
CMD ["sh", "docker-entrypoint.sh"]
|