From bb008f7f8f99bc37111315ed0967a4378bd7a7d3 Mon Sep 17 00:00:00 2001 From: jamesratcliffe101 Date: Mon, 1 Jun 2026 14:38:30 +0100 Subject: [PATCH] add a Dockerfile so app can be containerized --- .dockerignore | 134 ++++++++++++++++++++++++++++++++++++++++++++ Dockerfile | 112 ++++++++++++++++++++++++++++++++++++ compose.yml | 32 +++++++++++ pnpm-workspace.yaml | 3 + 4 files changed, 281 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 compose.yml create mode 100644 pnpm-workspace.yaml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c847070 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,134 @@ +############################################################ +# Production-ready .dockerignore for a Next.js (Vercel-style) app +# Keeps Docker builds fast, lean, and free of development files. +############################################################ + +# Dependencies (installed inside Docker, never copied) +node_modules/ +.pnpm-store/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# Next.js build outputs (always generated during `next build`) +.next/ +out/ +dist/ +build/ +.vercel/ + +# Tests and testing output (not needed in production images) +coverage/ +.nyc_output/ +__tests__/ +__mocks__/ +jest/ +cypress/ +cypress/screenshots/ +cypress/videos/ +playwright-report/ +test-results/ +.vitest/ +vitest.config.* +jest.config.* +cypress.config.* +playwright.config.* +*.test.* +*.spec.* + +# Local development and editor files +.git/ +.gitignore +.gitattributes +.vscode/ +.idea/ +*.swp +*.swo +*~ +*.log + +# Environment variables (only commit template files) +.env +.env*.local +.env.development +.env.test +.env.production.local + +# Docker configuration files (not needed inside build context) +Dockerfile* +.dockerignore +compose.yaml +compose.yml +docker-compose*.yaml +docker-compose*.yml + +# Documentation +*.md +docs/ + +# CI/CD configuration files +.github/ +.gitlab-ci.yml +.travis.yml +.circleci/ +Jenkinsfile + +# Cache directories and temporary data +.cache/ +.parcel-cache/ +.eslintcache +.stylelintcache +.swc/ +.turbo/ +.tmp/ +.temp/ + +# TypeScript build metadata +*.tsbuildinfo + +# Sensitive or unnecessary configuration files +*.pem +.editorconfig +.prettierrc* +prettier.config.* +.eslintrc* +eslint.config.* +.stylelintrc* +stylelint.config.* +.babelrc* +*.iml +*.ipr +*.iws + +# OS-specific junk +.DS_Store +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +Desktop.ini + +# AI/ML tool metadata and configs +.cursor/ +.cursorrules +.copilot/ +.copilotignore +.github/copilot/ +.gemini/ +.anthropic/ +.kiro +.claude +AGENTS.md +.agents/ + +# AI-generated temp files +*.aider* +*.copilot* +*.chatgpt* +*.claude* +*.gemini* +*.openai* +*.anthropic* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d326d64 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,112 @@ +# ============================================ +# Stage 1: Dependencies Installation Stage +# ============================================ + +# IMPORTANT: Node.js Version Maintenance +# This Dockerfile uses Node.js 24.13.0-slim, which was the latest LTS version at the time of writing. +# To ensure security and compatibility, regularly update the NODE_VERSION ARG to the latest LTS version. +ARG NODE_VERSION=24.13.0-slim + +FROM node:${NODE_VERSION} AS dependencies + +# Set working directory +WORKDIR /app + +# Copy package-related files first to leverage Docker's caching mechanism +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* pnpm-workspace.yaml ./ + +# Install project dependencies with frozen lockfile for reproducible builds +RUN --mount=type=cache,target=/root/.npm \ + --mount=type=cache,target=/usr/local/share/.cache/yarn \ + --mount=type=cache,target=/root/.local/share/pnpm/store \ + if [ -f package-lock.json ]; then \ + npm ci --no-audit --no-fund; \ + elif [ -f yarn.lock ]; then \ + corepack enable yarn && yarn install --frozen-lockfile --production=false; \ + elif [ -f pnpm-lock.yaml ]; then \ + corepack enable pnpm && pnpm install --frozen-lockfile; \ + else \ + echo "No lockfile found." && exit 1; \ + fi + +# ============================================ +# Stage 2: Build Next.js application in standalone mode +# ============================================ + +FROM node:${NODE_VERSION} AS builder + +# Set working directory +WORKDIR /app + +# Copy project dependencies from dependencies stage +COPY --from=dependencies /app/node_modules ./node_modules + +# Copy application source code +COPY . . + +ENV NODE_ENV=production + +# Next.js collects completely anonymous telemetry data about general usage. +# Learn more here: https://nextjs.org/telemetry +# Uncomment the following line in case you want to disable telemetry during the build. +ENV NEXT_TELEMETRY_DISABLED=1 + +# Build Next.js application +# If you want to speed up Docker rebuilds, you can cache the build artifacts +# by adding: --mount=type=cache,target=/app/.next/cache +# This caches the .next/cache directory across builds, but it also prevents +# .next/cache/fetch-cache from being included in the final image, meaning +# cached fetch responses from the build won't be available at runtime. +RUN if [ -f package-lock.json ]; then \ + npm run build; \ + elif [ -f yarn.lock ]; then \ + corepack enable yarn && yarn build; \ + elif [ -f pnpm-lock.yaml ]; then \ + corepack enable pnpm && pnpm build; \ + else \ + echo "No lockfile found." && exit 1; \ + fi + +# ============================================ +# Stage 3: Run Next.js application +# ============================================ + +FROM node:${NODE_VERSION} AS runner + +# Set working directory +WORKDIR /app + +# Set production environment variables +ENV NODE_ENV=production +ENV PORT=3000 +ENV HOSTNAME="0.0.0.0" + +# Next.js collects completely anonymous telemetry data about general usage. +# Learn more here: https://nextjs.org/telemetry +# Uncomment the following line in case you want to disable telemetry during the run time. +ENV NEXT_TELEMETRY_DISABLED=1 + +# Copy production assets +COPY --from=builder --chown=node:node /app/public ./public + +# Set the correct permission for prerender cache +RUN mkdir .next +RUN chown node:node .next + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=node:node /app/.next/standalone ./ +COPY --from=builder --chown=node:node /app/.next/static ./.next/static + +# If you want to persist the fetch cache generated during the build so that +# cached responses are available immediately on startup, uncomment this line: +# COPY --from=builder --chown=node:node /app/.next/cache ./.next/cache + +# Switch to non-root user for security best practices +USER node + +# Expose port 3000 to allow HTTP traffic +EXPOSE 3000 + +# Start Next.js standalone server +CMD ["node", "server.js"] \ No newline at end of file diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..e721cfd --- /dev/null +++ b/compose.yml @@ -0,0 +1,32 @@ +services: + # Node.js service (use with: docker compose up nextjs-standalone --build) + nextjs-standalone: + build: + context: . + dockerfile: Dockerfile + image: nextjs-standalone-image + container_name: nextjs-standalone-frontend + environment: + NODE_ENV: production + PORT: "3000" + ports: + - "3000:3000" + restart: unless-stopped + + # Bun service (use with: docker compose up nextjs-standalone-with-bun --build) + nextjs-standalone-with-bun: + build: + context: . + dockerfile: Dockerfile.bun + image: nextjs-standalone-bun-image + container_name: nextjs-standalone-bun-container + environment: + NODE_ENV: production + PORT: "3000" + ports: + - "3000:3000" + restart: unless-stopped + +networks: + traefik_proxy: + external: true \ No newline at end of file diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..eda297d --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +allowBuilds: + sharp@0.34.5: true + unrs-resolver@1.11.1: true \ No newline at end of file