استقرار Next.js با Docker، nginx و SSL
این راهنما یک الگوی رایج و تمیز برای استقرار برنامههای Node و Next.js است: برنامه در کانتینر روی 127.0.0.1 اجرا میشود، nginx در جلوی آن SSL و کش را مدیریت میکند و بهروزرسانی با یک دستور انجام میشود.
۱Dockerfile چندمرحلهای (ایمیج کوچک)
با حالت standalone در Next.js فقط فایلهای لازم به ایمیج نهایی میروند. در next.config مقدار output: 'standalone' را بگذارید.
# syntax=docker/dockerfile:1
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
FROM node:22-alpine AS builder
WORKDIR /app
ENV NEXT_TELEMETRY_DISABLED=1
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production PORT=3000 HOSTNAME=0.0.0.0
RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s CMD node -e "fetch('http://127.0.0.1:3000/').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
CMD ["node", "server.js"]۲docker-compose.prod.yml
پورت فقط روی loopback منتشر میشود تا از بیرون مستقیم در دسترس نباشد.
services:
app:
build: .
image: myapp:latest
container_name: myapp
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
environment:
NODE_ENV: production
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"۳بیلد و اجرا روی سرور
پروژه را به سرور منتقل کنید و بالا بیاورید.
rsync -az --delete --exclude node_modules --exclude .next --exclude .git ./ deploy@SERVER:/opt/myapp/
ssh deploy@SERVER 'cd /opt/myapp && docker compose -f docker-compose.prod.yml up -d --build'
ssh deploy@SERVER 'docker compose -f /opt/myapp/docker-compose.prod.yml ps'۴کانفیگ nginx (reverse proxy و کش)
فایلهای _next/static نام هشدار دارند و میتوانند یک سال کش شوند. بقیهٔ درخواستها به برنامه میروند.
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /_next/static/ {
proxy_pass http://127.0.0.1:3000;
expires 1y;
add_header Cache-Control "public, immutable";
}
}۵فعالسازی SSL
certbot کانفیگ را به HTTPS ارتقا میدهد و ریدایرکت را تنظیم میکند.
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d example.com -d www.example.com --redirect۶اسکریپت deploy یکدستوری
این اسکریپت را روی کامپیوتر خودتان بگذارید: کد را میفرستد، بیلد میکند، کانتینر را جایگزین میکند و سایت را بررسی میکند.
#!/usr/bin/env bash
set -euo pipefail
SERVER="deploy@203.0.113.10"
DIR="/opt/myapp"
rsync -az --delete --exclude node_modules --exclude .next --exclude .git ./ "$SERVER:$DIR/"
ssh "$SERVER" "cd $DIR && docker compose -f docker-compose.prod.yml build && docker compose -f docker-compose.prod.yml up -d"
sleep 5
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 20 https://example.com/)
[ "$code" = "200" ] && echo "deploy OK" || { echo "deploy FAILED ($code)"; exit 1; }۷لاگ و مانیتورینگ
لاگ برنامه و nginx و وضعیت سلامت کانتینر:
docker logs -f --tail 100 myapp
sudo tail -f /var/log/nginx/error.log
docker inspect -f '{{.State.Health.Status}}' myapp
docker stats --no-stream۸بازگشت به نسخهٔ قبل (Rollback)
قبل از هر deploy ایمیج فعلی را با یک برچسب نگه دارید تا در صورت مشکل سریع برگردید.
docker tag myapp:latest myapp:previous
# ... deploy جدید ...
# اگر مشکل بود:
docker tag myapp:previous myapp:latest
docker compose -f docker-compose.prod.yml up -dپرسشهای پرتکرار دربارهٔ استقرار Next.js با Docker، nginx و SSL
چرا پورت را فقط روی 127.0.0.1 منتشر میکنیم؟
تا برنامه مستقیم از اینترنت در دسترس نباشد و همهٔ ترافیک از nginx (با SSL و محدودیتها) بگذرد. همچنین Docker با -p ساده قوانین ufw را دور میزند و پورت را باز میکند.