Next.js uygulamasını Vercel dışında, kendi sunucunuzda çalıştırmak resmî olarak desteklenen bir yöntemdir. Next.js belgelerine göre uygulama bir Node.js sunucusu olarak dağıtıldığında tüm özellikler çalışır: Server Components, ISR, görsel optimizasyonu, Server Actions ve streaming. Statik dışa aktarma (output: 'export') ise sunucu gerektiren özellikleri desteklemez.

Bu rehberde Ubuntu 24.04 LTS bir VPS'e Docker kullanmadan Node.js kuracak, uygulamayı next build ile derleyecek, ister next start ister standalone çıktısıyla systemd altında çalıştıracak, önüne Nginx ve Let's Encrypt koyacağız. Önbellek, ISR, ortam değişkenleri ve güncelleme adımlarını Next.js'in self-hosting belgesine göre ele alacağız. Komutlar Next.js 16.4 belgeleriyle kontrol edildi.

Mimari ve gereksinimler

Mimari ve gereksinimler: Node.js, Önerilen Node.js hattı, Varsayılan port, Varsayılan host

Next.js belgeleri, Next.js sunucusunu doğrudan internete açmak yerine önüne Nginx gibi bir ters vekil koymayı önerir: hatalı istekler, yavaş bağlantı saldırıları, gövde boyutu sınırı ve hız sınırlama gibi işleri vekil üstlenir, Node.js süreci yalnızca render ile uğraşır. Bu yüzden uygulama 127.0.0.1:3000 üzerinde dinleyecek, dışarıya yalnızca Nginx 80 ve 443 portlarından açılacak. Kavram için reverse proxy nedir yazısına bakın.

BileşenDeğerKaynak
Node.jsEn az 20.9Next.js kurulum belgesi
Önerilen Node.js hattıAktif veya bakımdaki bir LTSNode.js sürüm takvimi
Varsayılan port3000 (-p veya PORT)next CLI belgesi
Varsayılan host0.0.0.0 (-H ile değişir)next CLI belgesi
Süreç yöneticisisystemd veya PM2Bu rehberin tercihi

Node.js sürüm takvimine göre Node.js 20, 30 Nisan 2026'da destek dışı kaldı. 1 Kasım 2026 itibarıyla Node.js 26 Aktif LTS, Node.js 24 ve 22 ise Bakım LTS aşamasındadır. Next.js asgari olarak 20.9 istese de destek dışı bir Node.js sürümüyle canlıya çıkmayın.

1. Node.js kurulumu

Node.js: Download sayfası Linux için nvm, fnm, Docker ve hazır ikili dosya seçenekleri sunar. Servis olarak çalışacak bir uygulamada yolun sabit olması işleri kolaylaştırdığı için resmî ikili arşivi /opt/node altına açacağız ve SHA-256 özetini doğrulayacağız. NODE_VER değerini nodejs.org'daki güncel LTS sürümüyle değiştirin.

bash
NODE_VER=v26.13.0   # nodejs.org adresindeki güncel LTS sürümünü yazın
cd /tmp
curl -fsSLO https://nodejs.org/dist/$NODE_VER/node-$NODE_VER-linux-x64.tar.xz
curl -fsSLO https://nodejs.org/dist/$NODE_VER/SHASUMS256.txt
grep "node-$NODE_VER-linux-x64.tar.xz" SHASUMS256.txt | sha256sum -c -

sudo mkdir -p /opt/node
sudo tar -xJf node-$NODE_VER-linux-x64.tar.xz -C /opt/node --strip-components=1
sudo ln -sf /opt/node/bin/node /usr/local/bin/node
sudo ln -sf /opt/node/bin/npm /usr/local/bin/npm
sudo ln -sf /opt/node/bin/npx /usr/local/bin/npx
node -v && npm -v

ARM tabanlı bir sunucudaysanız dosya adındaki linux-x64 kısmını linux-arm64 yapın. Node.js'i güncellemek için aynı adımları yeni sürümle tekrarlamanız yeterlidir. Node.js hakkında genel bilgi için Node.js nedir yazısına bakın.

2. Kullanıcı ve dizin düzeni

Uygulamayı ayrı ve giriş yetkisi olmayan bir kullanıcıyla çalıştırın. Her sürümü ayrı bir dizine kurup current adlı sembolik bağlantıyla yayına almak, hatalı bir sürümde geri dönmeyi tek komuta indirir.

bash
sudo adduser --system --group --home /srv/nextapp nextapp
sudo -u nextapp mkdir -p /srv/nextapp/releases

# /srv/nextapp/
#   releases/2026-11-01-1200/   derlenmiş sürüm
#   current -> releases/2026-11-01-1200
#   app.env                     sunucu tarafı gizli değerler

3. Derleme: next build

Next.js belgelerine göre package.json içinde build ve start betikleri bulunmalıdır (next build ve next start). Derleme için TypeScript gibi geliştirme bağımlılıkları da gerektiğinden derlemeden önce --omit=dev kullanmayın.

bash
REL=/srv/nextapp/releases/$(date +%Y-%m-%d-%H%M)
sudo -u nextapp git clone --depth 1 https://github.com/KULLANICI/PROJE.git $REL
cd $REL
sudo -u nextapp npm ci
sudo -u nextapp NODE_ENV=production npm run build
sudo -u nextapp ln -sfn $REL /srv/nextapp/current

Ortam değişkenleri konusunda belgelerdeki kuralı bilmek önemlidir: değişkenler varsayılan olarak yalnızca sunucuda kullanılabilir. Tarayıcıya açılması gerekenler NEXT_PUBLIC_ önekini taşır ve next build sırasında JavaScript paketine gömülür. Yani bir NEXT_PUBLIC_ değerini değiştirdiğinizde yeniden derlemeniz gerekir. Sunucu tarafı gizli değerler (veritabanı adresi, API anahtarları) çalışma anında okunabilir; bunları /srv/nextapp/app.env dosyasına yazın.

bash
sudo -u nextapp tee /srv/nextapp/app.env >/dev/null <<'EOF'
DATABASE_URL=postgresql://nextapp:GUCLU_BIR_SIFRE@127.0.0.1:5432/nextapp
API_SECRET=GUCLU_BIR_SIFRE
EOF
sudo chmod 600 /srv/nextapp/app.env

4a. next start ile systemd servisi

4a. next start ile systemd servisi

En basit ve tüm özellikleri destekleyen yol next start kullanmaktır. CLI belgesine göre varsayılan host 0.0.0.0 olduğu için -H 127.0.0.1 vererek uygulamayı yalnızca yerelde dinletiyoruz. Belgeler ayrıca PORT değerinin Next.js'in .env dosyasından ayarlanamayacağını, çünkü HTTP sunucusunun her şeyden önce açıldığını belirtir; portu komut satırında veriyoruz.

bash
sudo nano /etc/systemd/system/nextapp.service
ini
[Unit]
Description=Next.js uygulamasi
After=network.target

[Service]
Type=simple
User=nextapp
Group=nextapp
WorkingDirectory=/srv/nextapp/current
Environment=NODE_ENV=production
Environment=PATH=/opt/node/bin:/usr/bin:/bin
EnvironmentFile=/srv/nextapp/app.env
ExecStart=/srv/nextapp/current/node_modules/.bin/next start -H 127.0.0.1 -p 3000
Restart=on-failure
RestartSec=5
TimeoutStopSec=30

[Install]
WantedBy=multi-user.target
bash
sudo systemctl daemon-reload
sudo systemctl enable --now nextapp
systemctl status nextapp
curl -I http://127.0.0.1:3000

TimeoutStopSec=30 bilinçli bir seçim: Next.js belgelerine göre sunucu SIGTERM veya SIGINT aldığında devam eden istekleri ve bekleyen after() geri çağrılarını bitirip kapanır; bunun için 10-30 saniyelik bir boşaltma süresi önerilir. systemd varsayılan olarak SIGTERM gönderir. Birim dosyası ayrıntıları için systemd servis dosyası rehberine bakın.

4b. Alternatif: standalone çıktı

next.config.js içinde output: 'standalone' ayarlarsanız Next.js, derleme sırasında gereken dosyaları izleyerek .next/standalone klasörünü üretir. Bu klasör node_modules kurmadan tek başına dağıtılabilir ve next start yerine kullanılacak küçük bir server.js içerir. Sunucuya yalnızca derlenmiş çıktıyı göndermek istediğinizde kullanışlıdır.

js
module.exports = {
  output: 'standalone',
}

Belgelere göre bu küçük sunucu public ve .next/static klasörlerini varsayılan olarak kopyalamaz; derlemeden sonra elle kopyalamanız gerekir. Kopyalamazsanız sayfa açılır ama CSS, JavaScript ve görseller 404 döner.

bash
cp -r public .next/standalone/ && cp -r .next/static .next/standalone/.next/

systemd biriminde yalnızca şu satırlar değişir. server.js portu ve adresi PORT ile HOSTNAME ortam değişkenlerinden okur:

ini
Environment=PORT=3000
Environment=HOSTNAME=127.0.0.1
ExecStart=/opt/node/bin/node /srv/nextapp/current/.next/standalone/server.js

PM2 tercih ederseniz

PM2 kullanıyorsanız aynı mantık geçerlidir: süreç yerelde dinlemeli ve sunucu açılışında otomatik başlamalıdır. PM2 belgelerine göre açılış betiği pm2 startup ile üretilir, çalışan süreç listesi pm2 save ile kaydedilir. Cluster modu, log döndürme ve ecosystem.config.js ayrıntıları için Node.js Cluster ve PM2 rehberine bakın.

bash
cd /srv/nextapp/current
PORT=3000 HOSTNAME=127.0.0.1 pm2 start .next/standalone/server.js --name nextapp
pm2 startup     # çıktıdaki sudo komutunu çalıştırın
pm2 save

5. Nginx ters vekil

bash
sudo nano /etc/nginx/sites-available/nextapp
nginx
server {
    listen 80;
    listen [::]:80;
    server_name ornek.com www.ornek.com;

    client_max_body_size 10m;

    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;
    }
}
bash
sudo ln -s /etc/nginx/sites-available/nextapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

# HTTPS
sudo ufw allow 'Nginx Full'
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/local/bin/certbot
sudo certbot --nginx -d ornek.com -d www.ornek.com

Başlıkların anlamı Nginx server block rehberinde, sertifika ayrıntıları Let's Encrypt kurulumu yazısında anlatılıyor. Port 3000'i güvenlik duvarında açmanıza gerek yoktur; açmayın.

Streaming ve Nginx tamponlama

App Router, loading.js ve Suspense ile yanıtı parça parça gönderebilir. Next.js belgeleri, Nginx tamponlama yaparsa streaming'in çalışmayacağını ve bunu kapatmak için yanıtlara X-Accel-Buffering: no başlığı eklenebileceğini gösterir:

js
module.exports = {
  async headers() {
    return [
      {
        source: '/:path*{/}?',
        headers: [{ key: 'X-Accel-Buffering', value: 'no' }],
      },
    ]
  },
}

App Router temelleri ve Suspense kullanımı için Next.js App Router yazısına bakabilirsiniz.

6. Önbellek, ISR ve görseller

6. Önbellek, ISR ve görseller: Değişmez dosyalar, Dinamik sayfalar, Birden çok sunucu, Görsel optimizasyonu

Self-hosting belgesine göre ISR ve önbelleğe alınmış sayfalar aynı Next.js sunucu önbelleğini kullanır ve bu önbellek varsayılan olarak her sunucunun yerel diskinde tutulur; ayrıca varsayılan 50 MB'lık bir bellek içi önbellek vardır. Kalıcı diski olan tek bir next start sunucusunda bu düzen ek ayar olmadan çalışır.

  • Değişmez dosyalar: Dosya adında özet bulunan statik dosyalara Next.js public, max-age=31536000, immutable başlığını kendisi ekler; bu değiştirilemez.
  • Dinamik sayfalar: Kullanıcıya özel veri önbelleğe alınmasın diye private, no-cache, no-store, max-age=0, must-revalidate başlığı gönderilir. Nginx'te bu yanıtları önbelleğe almaya çalışmayın.
  • Birden çok sunucu: Önbellek sunucular arasında paylaşılmaz. Belgeler bu durumda cacheHandler ile Redis gibi ortak bir depoya geçmeyi, cacheMaxMemorySize: 0 ile bellek içi önbelleği kapatmayı, tüm sunucularda aynı NEXT_SERVER_ACTIONS_ENCRYPTION_KEY değerini kullanmayı ve deploymentId tanımlamayı önerir. Aksi hâlde "Failed to find Server Action" hataları görülebilir.
  • Görsel optimizasyonu: next/image, next start ile ek ayar gerektirmeden çalışır. Belgeler, glibc tabanlı Linux sistemlerde aşırı bellek kullanımını önlemek için ek yapılandırma gerekebileceğini not eder; bellek tüketimi beklenmedik şekilde artarsa bu notu inceleyin.

Yatay ölçekleme ve yük dağıtımı düşünüyorsanız yük dengeleme yazısı iyi bir başlangıçtır.

7. Güncelleme ve geri dönüş

Çalışan sürümün dizininde npm run build yapmak, derleme sürerken .next klasörünü değiştirdiği için sorun çıkarabilir. Yeni sürümü ayrı bir dizinde derleyip bağlantıyı çevirmek daha güvenlidir:

bash
REL=/srv/nextapp/releases/$(date +%Y-%m-%d-%H%M)
sudo -u nextapp git clone --depth 1 https://github.com/KULLANICI/PROJE.git $REL
cd $REL && sudo -u nextapp npm ci && sudo -u nextapp NODE_ENV=production npm run build
sudo -u nextapp ln -sfn $REL /srv/nextapp/current
sudo systemctl restart nextapp

# Geri dönüş: bağlantıyı önceki sürüme çevirip yeniden başlatın
ls /srv/nextapp/releases
sudo -u nextapp ln -sfn /srv/nextapp/releases/ONCEKI_SURUM /srv/nextapp/current
sudo systemctl restart nextapp

Tek süreçli bir kurulumda yeniden başlatma birkaç saniyelik kesinti yaratır. Bunu sıfıra indirmek için iki örneği farklı portlarda çalıştırıp Nginx upstream ile sırayla yenileyebilir veya PM2'nin reload komutunu kullanabilirsiniz. Eski sürüm dizinlerini zaman zaman silin; disk dolmasın. Bu akışı otomatikleştirmek için GitHub Actions ile VPS deploy rehberine bakın.

8. Loglar

bash
journalctl -u nextapp -f             # uygulama çıktısı
journalctl -u nextapp --since "1 hour ago"
sudo tail -f /var/log/nginx/error.log

Journald boyut sınırları ve Nginx loglarının döndürülmesi için logrotate rehberine bakın.

Sık karşılaşılan hatalar

  • 502 Bad Gateway: Uygulama çalışmıyor, çöktü veya farklı bir portta dinliyor. systemctl status nextapp ve ss -ltnp | grep 3000 ile kontrol edin. Ayrıntılı teşhis için 502/504 rehberi.
  • EADDRINUSE: address already in use: Port 3000'i başka bir süreç tutuyor; genellikle elle başlatılıp unutulan bir npm run start. ss -ltnp ile bulun.
  • Derleme "Killed" ile bitiyor: Bellek yetmedi. Swap ekleyin veya derlemeyi CI'da yapın.
  • Değişken değiştirdim ama sitede eski değer görünüyor: NEXT_PUBLIC_ değerleri derleme anında gömülür; yeniden derleyin. Sunucu tarafı değerler için servisi yeniden başlatın.
  • Standalone'da CSS ve JS 404: public ve .next/static klasörleri standalone dizinine kopyalanmamış.
  • Streaming çalışmıyor, sayfa bir anda geliyor: Nginx yanıtı tamponluyor; X-Accel-Buffering: no başlığını ekleyin.

Sık Sorulan Sorular

next start mı, standalone mı?

İkisi de tüm Node.js özelliklerini destekler. Derlemeyi sunucuda yapıyorsanız next start daha basittir. Derlemeyi CI'da yapıp sunucuya yalnızca çıktıyı gönderecekseniz standalone daha az dosya ve node_modules kurulumu olmadan çalışma imkânı sunar.

Docker ile yayınlamak daha mı iyi?

Next.js belgeleri Docker'ı da tüm özellikleri destekleyen bir seçenek olarak listeler ve standalone çıktılı örnek bir Dockerfile sunar. Sunucunuzda zaten Docker kullanıyorsanız Docker ile uygulama deploy yazısı daha uygun olabilir.

Aynı sunucuda birden fazla Next.js uygulaması çalıştırabilir miyim?

Evet. Her uygulamaya ayrı kullanıcı, ayrı port (3000, 3001 gibi), ayrı systemd birimi ve ayrı bir Nginx server block verin. Bellek kullanımını sunucu izleme araçlarıyla takip edin.

Next.js sunucusunu doğrudan 443'te çalıştırsam olmaz mı?

Teknik olarak mümkün olsa da Next.js belgeleri önüne bir ters vekil koymayı önerir. Ayrıca 1024 altındaki portlar için ek yetki gerekir; TLS ve sertifika yenilemesini Nginx ile Certbot'a bırakmak daha sade bir düzendir.

Yazılım ve hizmet adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen projeler veya şirketlerle ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Laravel VPS deploy · Django + Gunicorn + Nginx · FastAPI + Uvicorn deploy · GitHub Actions ile VPS deploy · Node.js Cluster ve PM2 · Next.js App Router · Reverse proxy nedir · systemd servis dosyası