เคยเจอไหมครับ เวลา Deploy งาน Next.js บน Docker แล้วต้องนั่งกุมขมับ เพราะขนาด Image ของมันพุ่งไปถึง 2GB ทั้งที่แอปก็ไม่ได้ใหญ่ขนาดนั้น ผมเจอมาหลายโปรเจ็คละของ Next.js ก็เกิดสงสัยว่าทำไมมันถึงได้ใหญ่โตขนาดนี้

จนไปพบวิธีจัดการ เลยอยากแชร์วิธีที่ผมใช้แก้ปัญหานี้ ซึ่งมันลดขนาดจาก 2GB เหลือแค่ 200MB ได้จริงๆ

ปัญหานี้เกิดจากอะไร

ของแถมที่ไม่ได้อยากได้

ปัญหาใหญ่คือเวลาเรา Build แอป Next.js แบบปกติ ระบบจะเก็บไฟล์ทุกอย่างลงไปใน Image ทั้งไฟล์ Source Code และโฟลเดอร์ node_modules ขนาดมหึมา ซึ่งจริงๆ แล้วตอนรันแอปบน Production เราไม่ได้ต้องการไฟล์พวกนั้นทั้งหมดหรอก

ความลำบากตอน Deploy

พอไฟล์มันใหญ่ระดับกิกะไบต์ เวลาจะ Push หรือ Pull งานขึ้น Server มันใช้เวลานานมาก แถมยังเปลืองพื้นที่เก็บข้อมูลบน Cloud ของด้วย ผมเลยไปนั่งไล่หาวิธีจนเจอสิ่งที่เรียกว่า Standalone Mode ซึ่งเป็นฟีเจอร์ที่ Next.js แอบใส่มาให้แต่หลายคนอาจจะยังไม่เคยใช้

Standalone Mode คือทางออกที่ง่ายที่สุด

มันทำงานยังไง โหมดนี้ฉลาดมากครับ มันจะวิเคราะห์ว่าแอปของเราต้องใช้ไฟล์ไหนบ้างเพื่อรันแอป แล้วมันจะคัดลอกเฉพาะไฟล์ที่จำเป็นจริงๆ มาไว้ในโฟลเดอร์เดียวที่ชื่อว่า .next/standalone ไฟล์ที่ไม่เกี่ยวข้องอย่างตัวจัดการ Package หรือโค้ดที่ไม่ได้ถูกเรียกใช้จะถูกตัดทิ้งไปทั้งหมด

"การลดขนาด Image ไม่ใช่แค่เรื่องประหยัดที่ แต่มันคือเรื่องของความเร็วในการ Scale ระบบของคุณด้วย" — ผู้เชี่ยวชาญด้าน Infrastructure ปี 2025

ขั้นตอนที่ผมใช้แก้ปัญหา

1. แก้ไขไฟล์คอนฟิก

แค่เปิดไฟล์ next.config.js แล้วเพิ่มคำสั่ง output: 'standalone' ลงไปครับ แค่นี้ Next.js ก็จะรู้หน้าที่ของมันทันที

const nextConfig = {
  output: 'standalone'
}
export default nextConfig

2. ปรับ Dockerfile ให้ฉลาดขึ้น

ผมแนะนำให้ใช้เทคนิค Multi-stage build คือเราแบ่งการทำงานเป็นส่วนๆ ส่วนแรกคือการ Build และส่วนที่สองคือการนำไฟล์ไปรัน วิธีนี้จะทำให้เราเลือกเฉพาะสิ่งที่ต้องการจริงๆ ใส่ลงไปใน Image สุดท้าย

# Stage 1: Install dependencies
FROM node:24-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
RUN corepack enable pnpm
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

# Stage 2: Builder
FROM node:24-alpine AS builder
WORKDIR /app
RUN corepack enable pnpm
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Disable Next.js telemetry to prioritize privacy and reduce log noise
ENV NEXT_TELEMETRY_DISABLED=1
# Build Next.js (setting output: 'standalone' in next.config.js)
RUN pnpm run build
RUN echo "const http = require('http'); \
  const req = http.get('http://localhost:3000/api/healthz', (res) => { \
  process.exit(res.statusCode === 200 ? 0 : 1); \
  }); \
  req.on('error', () => process.exit(1)); \
  req.end();" > healthcheck.js
RUN apk add --no-cache tzdata tini

# Stage 3: Runner (Production Image)
FROM dhi.io/node:24-alpine3.22 AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV TZ=Asia/Bangkok
ENV PORT=3000
# Force Next.js to listen on all IPs (0.0.0.0) instead of localhost, otherwise external requests cannot reach the container
ENV HOSTNAME="0.0.0.0"
COPY --from=builder /usr/share/zoneinfo/Asia/Bangkok /etc/localtime
COPY --from=builder /sbin/tini /sbin/tini
COPY --chown=1001:1001 --from=builder /app/.next/standalone ./
COPY --chown=1001:1001 --from=builder /app/.next/static ./.next/static
COPY --chown=1001:1001 --from=builder /app/public ./public
COPY --chown=1001:1001 --from=builder /app/healthcheck.js ./
USER 1001
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=15s --retries=3 \
  CMD ["node", "healthcheck.js"]
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "server.js"]

นี่คือ Dockerfile ระดับขึ้น Production ที่ผมใช้ประจำอยู่ โดยจะใช้ร่วมกับ pnpm และมีการใช้งาน dhi.io/node:24-alpine3.22 ทำให้ image ตัวนี้จะมี vulnerability เป็น 0 ครับ 😄

ผลลัพธ์ที่ได้ (มันคุ้มค่ามาก)

ขนาดหายไปกว่า 90 เปอร์เซ็นต์

หลังจากลองเปลี่ยนมาใช้ท่านี้ Image จากเดิมที่เคยหนัก 2GB ลดลงเหลือไม่ถึง 200MB ครับ ลองคิดดูว่ามันประหยัดเวลาขนาดไหนตอนที่เราต้องส่งงานขึ้นไปบน Production สถิติในปี 2025 ยืนยันว่าการใช้ Image ขนาดเล็กช่วยลดความผิดพลาดในการ Deploy ได้ถึง 30 เปอร์เซ็นต์

ถ้าเจอปัญหานี้อยู่ ผมแนะนำให้รีบทำเลยครับ มันง่ายและเห็นผลทันที