Self-hosted — VPS da deploy, Nginx sozlash

~20 daq · Next.js · Deployment va DevOps

Muammodan boshlaylik

VPS sotib oldingiz: Ubuntu, IP manzil, root paroli. Next.js ilovani shu yerga chiqarish kerak:

  • npm run start — SSH yopilsa to'xtaydi
  • http://203.0.113.10:3000 — port bilan, HTTPS siz
  • Server qayta yuklansa — ilova ishga tushmaydi
  • Bir nechta sayt bitta serverda kerak

Hayotiy o'xshatma

O'z do'koningizni ochish. Ijara savdo markazida (Vercel) — qo'riqchi, tozalash, chiroq — hammasi tayyor. O'z binongizda — eshik qulfi (firewall), qo'riqchi (Nginx), kalit (SSL), avariya generatori (PM2) — hammasini o'zingiz o'rnatasiz. Ko'proq ish, lekin to'liq nazorat va arzonroq.


Umumiy sxema

Internet ──HTTPS:443──▶ Nginx ──HTTP──▶ Next.js :3000 (PM2 yoki Docker)
                          │
                          └── /_next/static → diskdan, uzoq kesh

1. Server tayyorlash

ssh root@203.0.113.10

adduser deploy && usermod -aG sudo deploy
# lokal kompyuterda: ssh-copy-id deploy@203.0.113.10
# keyin /etc/ssh/sshd_config: PermitRootLogin no, PasswordAuthentication no

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

3000-port ochilmaydi — faqat Nginx orqali.

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs nginx git
sudo npm install -g pm2

2. Ilovani joylash — PM2 bilan

sudo mkdir -p /var/www/dokon && sudo chown deploy:deploy /var/www/dokon
cd /var/www/dokon
git clone git@github.com:user/dokon.git .

cp .env.example .env.production.local   # qiymatlarni to'ldiring
chmod 600 .env.production.local

npm ci
npx prisma migrate deploy
npm run build
// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'dokon',
    script: 'node_modules/next/dist/bin/next',
    args: 'start -p 3000 -H 127.0.0.1',
    instances: 2,
    exec_mode: 'cluster',
    max_memory_restart: '800M',
    env: { NODE_ENV: 'production' },
  }],
}
pm2 start ecosystem.config.js
pm2 startup     # chiqargan buyruqni bajaring
pm2 save
  • -H 127.0.0.1 — faqat ichki manzilda tinglash
  • cluster — bir nechta nusxa, CPU yadrolaridan foydalanish
  • Standalone (67-dars) bo'lsa: script: '.next/standalone/server.js' (static va public ni nusxalashni unutmang)

[!INFO] Docker bilan (67-dars) bo'lsa — PM2 o'rniga docker compose up -d, restart: unless-stopped. Nginx sozlamasi bir xil.


3. Nginx

# /etc/nginx/sites-available/dokon
upstream nextjs {
    server 127.0.0.1:3000;
    keepalive 64;
}

server {
    listen 80;
    server_name dokon.uz www.dokon.uz;

    client_max_body_size 10M;
    server_tokens off;

    gzip on;
    gzip_types text/css application/javascript application/json image/svg+xml;

    # Next.js statik fayllar — hash li nomlar, abadiy kesh
    location /_next/static/ {
        alias /var/www/dokon/.next/static/;
        expires 365d;
        add_header Cache-Control "public, max-age=31536000, immutable";
        access_log off;
    }

    location / {
        proxy_pass http://nextjs;
        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;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_buffering off;          # streaming (Suspense) uchun
    }
}
sudo ln -s /etc/nginx/sites-available/dokon /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
  • /_next/static/ — Nginx to'g'ridan-to'g'ri diskdan beradi, Node.js yuklanmaydi
  • proxy_buffering off — streaming javoblar (Suspense, 15-dars) bo'laklab yetib borishi uchun
  • X-Forwarded-* — ilova haqiqiy IP va protokolni bilishi uchun

4. SSL — Let's Encrypt

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d dokon.uz -d www.dokon.uz

Certbot sertifikatni oladi, Nginx ga 443 ni qo'shadi, HTTP → HTTPS yo'naltiradi va avtomatik yangilaydi.

Auth.js uchun

# .env.production.local
AUTH_URL=https://dokon.uz
AUTH_TRUST_HOST=true

Proxy ortida Auth.js haqiqiy manzilni bilishi uchun — aks holda OAuth callback http://127.0.0.1:3000 ga qaytadi (40-dars).


5. Yangilash

cd /var/www/dokon
git pull --ff-only
npm ci
npx prisma migrate deploy
npm run build
pm2 reload dokon

pm2 reload — cluster nusxalarini birma-bir qayta ishga tushiradi, sayt to'xtamaydi. Avtomatik — GitHub Actions orqali (69-dars).

[!WARNING] Build serverning o'zida bajarilganda npm run build vaqtida ko'p RAM va CPU ishlatiladi — kichik VPS (1–2 GB) da sayt sekinlashishi yoki build "out of memory" bilan yiqilishi mumkin. Yechimlar: swap fayl qo'shish, build ni CI da bajarib tayyor natijani serverga yuborish yoki Docker image ni CI da build qilish.


Self-hosted da Next.js imkoniyatlari

Imkoniyat Ishlaydimi E'tibor
SSR, Server Action, Route Handler ✅ —
ISR, revalidatePath/Tag ✅ Kesh diskda; bir nechta server — umumiy cacheHandler kerak
next/image ✅ sharp, CPU; katta trafikda CDN
Middleware ✅ —
Streaming ✅ Nginx proxy_buffering off

[!TIP] Statik fayllar va rasmlar uchun oldiga Cloudflare (bepul tarif) qo'yish — butun dunyo bo'ylab CDN, DDoS himoyasi va serveringiz yukini kamaytirish. DNS ni Cloudflare ga o'tkazib, "proxied" rejimni yoqing.


Tez-tez uchraydigan xatolar

1. 3000-port internetga ochiq

Nginx va SSL ni chetlab o'tish mumkin. ufw va -H 127.0.0.1.

2. Streaming ishlamaydi — sahifa bir vaqtda chiqadi

Nginx javobni buferlaydi — proxy_buffering off.

3. .env.production.local git da yoki hamma o'qiy oladi

chmod 600, .gitignore.


Xulosa

Nginx (443, SSL) → Next.js 127.0.0.1:3000 (PM2 cluster yoki Docker)
ufw: SSH, 80, 443; root va parol bilan kirish — o'chiq
PM2: ecosystem.config.js, startup + save, reload — to'xtashsiz
Nginx: /_next/static — diskdan, immutable kesh; proxy_buffering off; X-Forwarded-*
Certbot — bepul HTTPS; AUTH_URL + AUTH_TRUST_HOST
Yangilash: pull → ci → migrate → build → reload
Kichik VPS — build uchun swap yoki CI da build

Keyingi dars

69-dars: CI/CD — GitHub Actions bilan avtomatik deploy

Har bir push da lint, test va build — va muvaffaqiyatli bo'lsa avtomatik deploy.