İçeriğe geç
nextjs

Next.js Docker ile Deployment Rehberi

Tarık Tunç
Next.js Docker ile Deployment Rehberi

Next.js Docker ile Deployment Rehberi

Docker, uygulamalarınızı tutarlı ve tekrarlanabilir ortamlarda çalıştırmanızı sağlayan konteynerizasyon teknolojisidir. Next.js projelerinizi Docker ile deploy etmek, geliştirme ve production ortamları arasındaki farklılıkları ortadan kaldırır. Bu rehberde Next.js uygulamanız için optimize edilmiş Docker image oluşturmayı, multi-stage build kullanmayı ve production'a hazır deployment stratejilerini adım adım inceleyeceğiz.

Neden Docker ile Next.js?

  • Tutarlı ortam: Geliştirici makineleri, CI/CD ve production aynı ortamda çalışır
  • Kolay ölçeklendirme: Kubernetes, Docker Swarm gibi orchestration araçlarıyla yatay ölçekleme
  • İzolasyon: Her uygulama kendi bağımlılıklarıyla izole çalışır
  • Tekrarlanabilir build: Aynı Dockerfile her zaman aynı sonucu üretir
  • Hızlı deployment: Image bir kez build edilir, her yere deploy edilir

Temel Dockerfile

Basit bir Next.js Dockerfile ile başlayalım:

# Dockerfile
FROM node:20-alpine AS base

WORKDIR /app

# Bağımlılıkları kopyala ve yükle
COPY package.json package-lock.json ./
RUN npm ci

# Uygulama kodunu kopyala
COPY . .

# Build
RUN npm run build

# Production
EXPOSE 3000
ENV NODE_ENV=production

CMD ["npm", "start"]
Uyarı: Bu temel Dockerfile çalışır ancak image boyutu büyük olur. Production için aşağıdaki multi-stage build yaklaşımını kullanın.

Multi-Stage Build ile Optimize Edilmiş Dockerfile

Next.js'in resmi olarak önerdiği multi-stage build yaklaşımı, image boyutunu önemli ölçüde küçültür:

# Dockerfile
FROM node:20-alpine AS base

# 1. Bağımlılık aşaması
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci --only=production

# 2. Build aşaması
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Next.js telemetriyi devre dışı bırak
ENV NEXT_TELEMETRY_DISABLED=1

RUN npm run build

# 3. Production aşaması
FROM base AS runner
WORKDIR /app

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

# Güvenlik: root olmayan kullanıcı oluştur
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Public dosyalarını kopyala
COPY --from=builder /app/public ./public

# Standalone output kopyala
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

CMD ["node", "server.js"]

next.config.js Ayarları

Multi-stage build için Next.js'in standalone output modunu etkinleştirin:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
};

module.exports = nextConfig;
Bilgi: output: 'standalone' ayarı, Next.js'in yalnızca production için gerekli dosyaları içeren minimal bir klasör oluşturmasını sağlar. Bu sayede node_modules'un tamamını kopyalamanıza gerek kalmaz.

.dockerignore Dosyası

Gereksiz dosyaların Docker context'ine dahil edilmesini önleyin:

# .dockerignore
node_modules
.next
.git
.gitignore
README.md
docker-compose*.yml
Dockerfile
.env*.local
.vscode
.husky
coverage
__tests__

Docker Compose ile Geliştirme Ortamı

Geliştirme ortamında hot-reload ile çalışmak için Docker Compose kullanabilirsiniz:

# docker-compose.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - '3000:3000'
    volumes:
      - .:/app
      - /app/node_modules
      - /app/.next
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:password@db:5432/mydb
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    ports:
      - '5432:5432'
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydb
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
# Dockerfile.dev
FROM node:20-alpine

WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install

COPY . .

EXPOSE 3000
CMD ["npm", "run", "dev"]

Production Docker Compose

# docker-compose.prod.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: runner
    ports:
      - '3000:3000'
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
      - NEXTAUTH_URL=${NEXTAUTH_URL}
    restart: unless-stopped
    healthcheck:
      test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:3000/api/health']
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  nginx:
    image: nginx:alpine
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - app
    restart: unless-stopped

Health Check Endpoint

Docker'ın uygulamanızın sağlığını kontrol edebilmesi için bir API endpoint oluşturun:

// app/api/health/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
  try {
    // Veritabanı bağlantısı kontrolü eklenebilir
    return NextResponse.json(
      {
        status: 'healthy',
        timestamp: new Date().toISOString(),
        uptime: process.uptime(),
      },
      { status: 200 }
    );
  } catch (error) {
    return NextResponse.json(
      { status: 'unhealthy', error: String(error) },
      { status: 503 }
    );
  }
}

CI/CD Pipeline ile Docker Build

GitHub Actions ile otomatik Docker build ve push:

# .github/workflows/docker.yml
name: Docker Build & Push

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and Push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ghcr.io/${{ github.repository }}:latest
            ghcr.io/${{ github.repository }}:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Image Boyutu Optimizasyonu

Docker image boyutunu küçültmek için şu stratejileri uygulayın:

  1. Alpine base image kullanın: node:20-alpine (~50MB) vs node:20 (~350MB)
  2. Multi-stage build: Build bağımlılıklarını final image'a dahil etmeyin
  3. Standalone output: Next.js'in minimal output modunu kullanın
  4. .dockerignore: Gereksiz dosyaları build context'inden çıkarın
  5. Layer caching: Sık değişen dosyaları Dockerfile'ın sonuna koyun
İpucu: docker images komutuyla image boyutlarını karşılaştırın. İyi optimize edilmiş bir Next.js standalone image genellikle 150-200MB civarında olmalıdır.

Sonuç

Docker ile Next.js deployment, uygulamanızı tutarlı, güvenli ve ölçeklenebilir şekilde çalıştırmanın en etkili yollarından biridir. Multi-stage build ile image boyutunu minimize edin, standalone output modunu etkinleştirin ve non-root kullanıcıyla çalıştırarak güvenliği artırın. Docker Compose ile hem geliştirme hem de production ortamlarınızı kolayca yönetebilirsiniz.

Kaynaklar

İlgili Yazılar