From 68a31c470a55cba3894fa8c45f9a6ce82c72c702 Mon Sep 17 00:00:00 2001 From: pentuhhh Date: Wed, 27 Aug 2025 22:03:36 +0800 Subject: [PATCH 1/2] feat: Add Docker containerization with production-ready deployment - Add multi-stage Dockerfile optimized for Next.js standalone output - Include Bun runtime for database operations (sync, seed, reset) - Configure Next.js for standalone mode with proper static asset handling - Add docker-compose.yml with host network mode for external DB connectivity - Include comprehensive Docker documentation in README.md - Support for external MySQL database connections - Add .dockerignore for optimized build context - Enable database management scripts within containerized environment Technical improvements: - Next.js standalone output for minimal production image - Proper static asset serving in Docker environment - Host network mode for reliable external database connectivity - Source files included for database operations - Bun configuration for TypeScript path alias resolution --- .dockerignore | 25 +++++++++++++++++++++ Dockerfile | 54 ++++++++++++++++++++++++++++++++++++++++++++++ README.md | 22 +++++++++++++++++++ bunfig.toml | 2 ++ docker-compose.yml | 20 +++++++++++++++++ next.config.ts | 4 +++- 6 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 bunfig.toml create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0ad802f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +# dependencies +node_modules +pnpm-lock.yaml +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# next +.next +out + +# env +.env +.env.* + +# misc +.DS_Store +.git +.gitignore +.cache + +# editor +.vscode +.idea + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e1f4555 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,54 @@ +# syntax=docker/dockerfile:1 + +FROM node:20-alpine AS deps +WORKDIR /app +RUN apk add --no-cache libc6-compat python3 make g++ +# Install bun globally +RUN npm install -g bun +COPY package.json package-lock.json* pnpm-lock.yaml* yarn.lock* ./ +RUN if [ -f pnpm-lock.yaml ]; then corepack enable && corepack prepare pnpm@9.12.3 --activate && pnpm install --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f yarn.lock ]; then corepack enable && corepack prepare yarn@1.22.22 --activate && yarn install --frozen-lockfile; \ + else npm install; fi + +FROM node:20-alpine AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . +ENV NEXT_TELEMETRY_DISABLED=1 +RUN npm run build + +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +# Create non-root user +RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001 + +# Copy package.json and public files +COPY --from=builder /app/package.json ./package.json +COPY --from=builder /app/public ./public +COPY --from=builder /app/tsconfig.json ./tsconfig.json +COPY --from=builder /app/bunfig.toml ./bunfig.toml + +# Copy Next.js build output - ensure static files are in the right place +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static + +# Copy source files needed for database operations +COPY --from=builder /app/src ./src + +# Copy bun from deps stage so it's available for database operations +COPY --from=deps /usr/local/bin/bun /usr/local/bin/bun +COPY --from=deps /usr/local/lib/node_modules/bun /usr/local/lib/node_modules/bun + +# Also include node_modules so db scripts can resolve runtime deps (e.g., sequelize) +COPY --from=deps /app/node_modules ./node_modules + +ENV PORT=3000 +EXPOSE 3000 +USER nextjs + +# Start the standalone server +CMD ["node", "server.js"] diff --git a/README.md b/README.md index 19a7240..4377382 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,28 @@ App will be running on [http://localhost:3000](http://localhost:3000). --- +#### Docker support + +**Build and Run** + ```bash + # Build the Docker image + docker build -t ciscode-app:latest . + + # Or use docker-compose (recommended) + docker compose up -d --build + ``` +**Database Connection** + + If you are locally hosting your database, it is recommended that you also use a container for mysql. + in the docker-compose.yml... + + ``` + network_mode: "host" + ``` + + ...denotes that the database must be locally hosted. it would still also use the db credentials that you input in the .env + + ## 🤝 Contributing Contributions are welcome! Feel free to fork the repo, submit issues, or open pull requests. diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..1e1938c --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[alias] +"@/" = "./src/" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..cb5f44a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.9" +services: + app: + build: . + image: ciscode-app:latest + environment: + - NODE_ENV=production + - PORT=3049 + - DB_HOST=${DB_HOST} + - DB_PORT=${DB_PORT} + - DB_USER=${DB_USER} + - DB_PASSWORD=${DB_PASSWORD} + - DB_NAME=${DB_NAME} + - JWT_SECRET=${JWT_SECRET} + ports: + - "3049:3049" + restart: unless-stopped + env_file: + - .env + network_mode: "host" # This gives access to host network \ No newline at end of file diff --git a/next.config.ts b/next.config.ts index e9ffa30..ed2939f 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,9 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + output: 'standalone', + assetPrefix: process.env.NODE_ENV === 'production' ? '' : undefined, + trailingSlash: false, }; export default nextConfig; From 8f7dac6a9de26378fc5d01fb8f165e5715823b88 Mon Sep 17 00:00:00 2001 From: pentuhhh Date: Thu, 28 Aug 2025 23:58:29 +0800 Subject: [PATCH 2/2] chore: Adjustments to Next.js container run reliably and support MariaDB - Align port handling to use ENV PORT; map "${PORT}:${PORT}" in compose - Switch to bridge networking with extra_hosts (host.docker.internal) - Include .env in image and allow it in build context (.dockerignore update) - Add Alpine build/runtime deps for native modules (node-pty, mysql2): libc6-compat, libstdc++, musl-dev, linux-headers, python3, make, g++, pkgconfig, ca-certificates, openssl, git, bash, curl - Install bun and ensure proper symlinks; copy bun into runtime stage - Use Next.js standalone output; copy src and node_modules for DB scripts - Update db scripts to `bun build --target=node` then run with node to avoid aborts - Use host.docker.internal for DB host; verified MariaDB connectivity - Remove obsolete compose version field; clean up port exposure (ENV/EXPOSE) Result: container starts on the configured PORT and `npm run db:sync` works against MariaDB. --- .dockerignore | 4 --- Dockerfile | 90 +++++++++++++++++++++++++++++++++++++++------- docker-compose.yml | 9 ++--- package.json | 6 ++-- 4 files changed, 86 insertions(+), 23 deletions(-) diff --git a/.dockerignore b/.dockerignore index 0ad802f..8dca062 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,10 +9,6 @@ yarn-error.log* .next out -# env -.env -.env.* - # misc .DS_Store .git diff --git a/Dockerfile b/Dockerfile index e1f4555..40b9906 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,52 +2,118 @@ FROM node:20-alpine AS deps WORKDIR /app -RUN apk add --no-cache libc6-compat python3 make g++ -# Install bun globally + +# Install system dependencies needed for native modules (especially node-pty) +RUN apk add --no-cache \ + libc6-compat \ + python3 \ + make \ + g++ \ + git \ + bash \ + curl \ + ca-certificates \ + openssl \ + pkgconfig \ + # Additional dependencies for native modules + linux-headers \ + # Dependencies for node-pty and other native packages + libstdc++ \ + # For better compatibility + musl-dev + +# Install bun globally with proper setup RUN npm install -g bun +# Ensure bun is properly linked and available +RUN ln -sf /usr/local/bin/bun /usr/bin/bun + +# Copy package files COPY package.json package-lock.json* pnpm-lock.yaml* yarn.lock* ./ -RUN if [ -f pnpm-lock.yaml ]; then corepack enable && corepack prepare pnpm@9.12.3 --activate && pnpm install --frozen-lockfile; \ - elif [ -f package-lock.json ]; then npm ci; \ - elif [ -f yarn.lock ]; then corepack enable && corepack prepare yarn@1.22.22 --activate && yarn install --frozen-lockfile; \ - else npm install; fi + +# Install dependencies with proper fallback +RUN if [ -f pnpm-lock.yaml ]; then \ + corepack enable && corepack prepare pnpm@9.12.3 --activate && pnpm install --frozen-lockfile; \ + elif [ -f package-lock.json ]; then \ + npm ci --build-from-source; \ + elif [ -f yarn.lock ]; then \ + corepack enable && corepack prepare yarn@1.22.22 --activate && yarn install --frozen-lockfile; \ + else \ + npm install --build-from-source; \ + fi FROM node:20-alpine AS builder WORKDIR /app + +# Install build dependencies for native modules +RUN apk add --no-cache \ + python3 \ + make \ + g++ \ + git \ + linux-headers \ + musl-dev \ + pkgconfig + COPY --from=deps /app/node_modules ./node_modules COPY . . + +# Set environment variables for build ENV NEXT_TELEMETRY_DISABLED=1 +ENV NODE_ENV=production + +# Build the application RUN npm run build FROM node:20-alpine AS runner WORKDIR /app + ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 +# Install runtime dependencies needed for native modules +RUN apk add --no-cache \ + libc6-compat \ + # Runtime dependencies for node-pty + libstdc++ \ + # For database connections + ca-certificates \ + # For better compatibility + musl + # Create non-root user RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001 -# Copy package.json and public files +# Copy package.json and configuration files COPY --from=builder /app/package.json ./package.json COPY --from=builder /app/public ./public COPY --from=builder /app/tsconfig.json ./tsconfig.json COPY --from=builder /app/bunfig.toml ./bunfig.toml -# Copy Next.js build output - ensure static files are in the right place +# Copy environment file for database connections +COPY .env .env + +# Copy Next.js build output COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/.next/static ./.next/static # Copy source files needed for database operations COPY --from=builder /app/src ./src -# Copy bun from deps stage so it's available for database operations +# Copy bun from deps stage with proper setup COPY --from=deps /usr/local/bin/bun /usr/local/bin/bun COPY --from=deps /usr/local/lib/node_modules/bun /usr/local/lib/node_modules/bun +# Ensure bun is accessible +RUN ln -sf /usr/local/bin/bun /usr/bin/bun -# Also include node_modules so db scripts can resolve runtime deps (e.g., sequelize) +# Copy node_modules for runtime dependencies COPY --from=deps /app/node_modules ./node_modules -ENV PORT=3000 -EXPOSE 3000 +# Set proper permissions +RUN chown -R nextjs:nodejs /app + +ENV PORT=${PORT:-3000} +EXPOSE ${PORT:-3000} + USER nextjs # Start the standalone server diff --git a/docker-compose.yml b/docker-compose.yml index cb5f44a..b59bb2a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,11 +1,10 @@ -version: "3.9" services: app: build: . image: ciscode-app:latest environment: - NODE_ENV=production - - PORT=3049 + - PORT=${PORT} - DB_HOST=${DB_HOST} - DB_PORT=${DB_PORT} - DB_USER=${DB_USER} @@ -13,8 +12,10 @@ services: - DB_NAME=${DB_NAME} - JWT_SECRET=${JWT_SECRET} ports: - - "3049:3049" + - "${PORT}:${PORT}" restart: unless-stopped env_file: - .env - network_mode: "host" # This gives access to host network \ No newline at end of file + # Using bridge network instead of host network for better port accessibility + extra_hosts: + - "host.docker.internal:host-gateway" \ No newline at end of file diff --git a/package.json b/package.json index a1abee5..7dafb28 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,9 @@ "build": "next build", "start": "dotenv -- next start", "lint": "next lint", - "db:sync": "bun run src/db/sync.ts", - "db:seed": "bun run src/db/seed.ts", - "db:reset": "bun run src/db/sync.ts && bun run src/db/seed.ts", + "db:sync": "bun build src/db/sync.ts --target=node --outfile=/tmp/sync.mjs && node /tmp/sync.mjs", + "db:seed": "bun build src/db/seed.ts --target=node --outfile=/tmp/seed.mjs && node /tmp/seed.mjs", + "db:reset": "bun build src/db/sync.ts --target=node --outfile=/tmp/sync.mjs && node /tmp/sync.mjs && bun build src/db/seed.ts --target=node --outfile=/tmp/seed.mjs && node /tmp/seed.mjs", "prepare": "husky install" }, "dependencies": {