112 lines
3.9 KiB
Docker
112 lines
3.9 KiB
Docker
# =============================================================================
|
|
# Frontend - Production Dockerfile
|
|
# =============================================================================
|
|
# Multi-stage build for optimized Next.js 15 deployment with SSR support
|
|
# Final image size: ~120-150MB (vs ~500MB+ without optimization)
|
|
# Node.js 22 LTS (Jod) with Alpine Linux 3.22
|
|
# =============================================================================
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Stage 1: Dependencies Installation
|
|
# -----------------------------------------------------------------------------
|
|
FROM node:22-alpine3.22 AS deps
|
|
|
|
# Install necessary system dependencies
|
|
RUN apk add --no-cache libc6-compat
|
|
|
|
WORKDIR /app
|
|
|
|
# Install pnpm globally
|
|
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
|
|
# Copy package management files
|
|
COPY package.json pnpm-lock.yaml .npmrc* ./
|
|
|
|
# Install dependencies using pnpm
|
|
# .npmrc ensures sharp is built correctly for image optimization
|
|
RUN pnpm install --frozen-lockfile --prod=false
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Stage 2: Application Builder
|
|
# -----------------------------------------------------------------------------
|
|
FROM node:22-alpine3.22 AS builder
|
|
|
|
WORKDIR /app
|
|
|
|
# Install pnpm
|
|
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
|
|
# Copy dependencies from deps stage
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
|
|
# Copy application source
|
|
COPY . .
|
|
|
|
# Build arguments for NEXT_PUBLIC_* environment variables
|
|
# These must be set at build time for client-side code
|
|
ARG NEXT_PUBLIC_APP_BASE_URL
|
|
ARG NEXT_PUBLIC_STYTCH_PUBLIC_TOKEN
|
|
ARG NEXT_PUBLIC_STYTCH_PROJECT_ID
|
|
ARG NEXT_PUBLIC_STYTCH_PROJECT_ENV
|
|
ARG NEXT_PUBLIC_POLAR_API_SANDBOX_ACCESS_TOKEN
|
|
ARG NEXT_PUBLIC_POLAR_API_PRODUCTION_ACCESS_TOKEN
|
|
ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
|
|
ARG NEXT_PUBLIC_UMAMI_SCRIPT_URL
|
|
|
|
# Set build-time environment variables
|
|
ENV NEXT_PUBLIC_APP_BASE_URL=$NEXT_PUBLIC_APP_BASE_URL
|
|
ENV NEXT_PUBLIC_STYTCH_PUBLIC_TOKEN=$NEXT_PUBLIC_STYTCH_PUBLIC_TOKEN
|
|
ENV NEXT_PUBLIC_STYTCH_PROJECT_ID=$NEXT_PUBLIC_STYTCH_PROJECT_ID
|
|
ENV NEXT_PUBLIC_STYTCH_PROJECT_ENV=$NEXT_PUBLIC_STYTCH_PROJECT_ENV
|
|
ENV NEXT_PUBLIC_POLAR_API_SANDBOX_ACCESS_TOKEN=$NEXT_PUBLIC_POLAR_API_SANDBOX_ACCESS_TOKEN
|
|
ENV NEXT_PUBLIC_POLAR_API_PRODUCTION_ACCESS_TOKEN=$NEXT_PUBLIC_POLAR_API_PRODUCTION_ACCESS_TOKEN
|
|
ENV NEXT_PUBLIC_UMAMI_WEBSITE_ID=$NEXT_PUBLIC_UMAMI_WEBSITE_ID
|
|
ENV NEXT_PUBLIC_UMAMI_SCRIPT_URL=$NEXT_PUBLIC_UMAMI_SCRIPT_URL
|
|
|
|
# Disable Next.js telemetry
|
|
ENV NEXT_TELEMETRY_DISABLED=1
|
|
|
|
# Build the application
|
|
# next.config.ts already has output: "standalone" configured
|
|
RUN pnpm build
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Stage 3: Production Runtime
|
|
# -----------------------------------------------------------------------------
|
|
FROM node:22-alpine3.22 AS runner
|
|
|
|
WORKDIR /app
|
|
|
|
# Set production environment
|
|
ENV NODE_ENV=production
|
|
ENV NEXT_TELEMETRY_DISABLED=1
|
|
|
|
# Create non-root user for security
|
|
RUN addgroup --system --gid 1001 nodejs && \
|
|
adduser --system --uid 1001 nextjs
|
|
|
|
# Copy public assets
|
|
COPY --from=builder /app/public ./public
|
|
|
|
# Copy standalone build output (optimized by Next.js)
|
|
# This includes only necessary dependencies and files
|
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
|
|
|
# Switch to non-root user
|
|
USER nextjs
|
|
|
|
# Expose application port
|
|
EXPOSE 3000
|
|
|
|
# Set runtime environment variables
|
|
ENV PORT=3000
|
|
ENV HOSTNAME="0.0.0.0"
|
|
|
|
# Health check for container orchestration
|
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
|
|
CMD node -e "require('http').get('http://localhost:3000/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
|
|
|
# Start the application
|
|
# Use node directly (not npm/pnpm) for proper signal handling and graceful shutdown
|
|
CMD ["node", "server.js"]
|