From 70b728a474b7273f1bc3d62f26d16e0bf749a329 Mon Sep 17 00:00:00 2001 From: ianshaloom Date: Sun, 8 Mar 2026 02:02:58 +0300 Subject: [PATCH] feat(docker): add Dockerfiles and update next.config.js for admin and storefront applications - Introduced Dockerfiles for both admin and storefront applications to streamline the build and deployment process using multi-stage builds. - Configured the Dockerfiles to install dependencies, build the applications, and set up a minimal runtime environment. - Updated next.config.js for both applications to enable standalone output and set the outputFileTracingRoot for proper file tracing in a monorepo setup. This commit enhances the containerization of the applications, improving deployment efficiency and reducing image sizes. --- apps/admin/Dockerfile | 48 +++++++++++++++++++++++++++ apps/admin/next.config.js | 2 ++ apps/storefront/Dockerfile | 59 ++++++++++++++++++++++++++++++++++ apps/storefront/next.config.js | 4 +++ 4 files changed, 113 insertions(+) create mode 100644 apps/admin/Dockerfile create mode 100644 apps/storefront/Dockerfile diff --git a/apps/admin/Dockerfile b/apps/admin/Dockerfile new file mode 100644 index 0000000..5a747e4 --- /dev/null +++ b/apps/admin/Dockerfile @@ -0,0 +1,48 @@ +# Build context: ./out (turbo prune admin --docker) +# out/json/ — package.json files only → used by deps stage for layer caching +# out/full/ — full pruned monorepo → used by builder stage for source +# out/package-lock.json + +# ── Stage 1: deps ──────────────────────────────────────────────────────────── +FROM node:20-alpine AS deps + +RUN apk add --no-cache libc6-compat +WORKDIR /app + +COPY json/ . +COPY package-lock.json . +RUN npm ci + +# ── Stage 2: builder ───────────────────────────────────────────────────────── +FROM node:20-alpine AS builder + +WORKDIR /app + +COPY --from=deps /app/node_modules ./node_modules +COPY full/ . + +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN npx turbo build --filter=admin + +# ── Stage 3: runner ────────────────────────────────────────────────────────── +FROM node:20-alpine AS runner + +WORKDIR /app + +ENV NODE_ENV=production \ + NEXT_TELEMETRY_DISABLED=1 \ + HOSTNAME=0.0.0.0 \ + PORT=3001 + +RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001 + +COPY --from=builder --chown=nextjs:nodejs /app/apps/admin/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/apps/admin/.next/static ./apps/admin/.next/static +COPY --from=builder --chown=nextjs:nodejs /app/apps/admin/public ./apps/admin/public + +USER nextjs + +EXPOSE 3001 + +CMD ["node", "server.js"] diff --git a/apps/admin/next.config.js b/apps/admin/next.config.js index 5155e0c..5303395 100644 --- a/apps/admin/next.config.js +++ b/apps/admin/next.config.js @@ -3,6 +3,8 @@ const path = require("path"); /** @type {import('next').NextConfig} */ const nextConfig = { + output: "standalone", + outputFileTracingRoot: path.join(__dirname, "../.."), transpilePackages: ["@repo/convex", "@repo/types", "@repo/utils"], turbopack: { root: path.join(__dirname, "..", ".."), diff --git a/apps/storefront/Dockerfile b/apps/storefront/Dockerfile new file mode 100644 index 0000000..7e974af --- /dev/null +++ b/apps/storefront/Dockerfile @@ -0,0 +1,59 @@ +# Build context: ./out (turbo prune storefront --docker) +# out/json/ — package.json files only → used by deps stage for layer caching +# out/full/ — full pruned monorepo → used by builder stage for source +# out/package-lock.json + +# ── Stage 1: deps ──────────────────────────────────────────────────────────── +# Install ALL dependencies (dev + prod) using only the package.json tree. +# This layer is shared with the builder stage and only rebuilds when +# a package.json or the lock file changes — not when source code changes. +FROM node:20-alpine AS deps + +RUN apk add --no-cache libc6-compat +WORKDIR /app + +COPY json/ . +COPY package-lock.json . +RUN npm ci + +# ── Stage 2: builder ───────────────────────────────────────────────────────── +# Full monorepo source + build artifact. +# next build produces .next/standalone/ because output: "standalone" is set +# in next.config.js — that's what makes the runner stage small. +FROM node:20-alpine AS builder + +WORKDIR /app + +COPY --from=deps /app/node_modules ./node_modules +COPY full/ . + +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN npx turbo build --filter=storefront + +# ── Stage 3: runner ────────────────────────────────────────────────────────── +# Minimal runtime image — only the standalone bundle, static assets, and public dir. +# No source code, no dev dependencies, no build tools. +FROM node:20-alpine AS runner + +WORKDIR /app + +ENV NODE_ENV=production \ + NEXT_TELEMETRY_DISABLED=1 \ + HOSTNAME=0.0.0.0 \ + PORT=3000 + +# Non-root user for security +RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001 + +# standalone output mirrors the monorepo tree, so server.js lands at /app/server.js +# Static files and public/ must be copied separately — they are not in standalone/ +COPY --from=builder --chown=nextjs:nodejs /app/apps/storefront/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/apps/storefront/.next/static ./apps/storefront/.next/static +COPY --from=builder --chown=nextjs:nodejs /app/apps/storefront/public ./apps/storefront/public + +USER nextjs + +EXPOSE 3000 + +CMD ["node", "server.js"] diff --git a/apps/storefront/next.config.js b/apps/storefront/next.config.js index 763c3e5..299dd2e 100644 --- a/apps/storefront/next.config.js +++ b/apps/storefront/next.config.js @@ -3,6 +3,10 @@ const path = require("path"); /** @type {import('next').NextConfig} */ const nextConfig = { + output: "standalone", + // Required in a monorepo: tells Next.js to trace files from the repo root + // so the standalone bundle includes files from packages/ + outputFileTracingRoot: path.join(__dirname, "../.."), transpilePackages: ["@repo/convex", "@repo/types", "@repo/utils"], turbopack: { root: path.join(__dirname, "..", ".."),