PM2 + Nginx ters proxy: Node.js uygulamasını 80 numaralı porta bağlama

Test ortamıDebian ailesiUbuntu 24.04.5 LTSnormal kullanıcı hesabı
Node.js uygulama işletimi ve ters proxy çalışması doğrulandı

Ubuntu 24.04 sunucusuna Node.js PM2 Nginx ters proxy yapısını kurduk: uygulamayı PM2 çalıştırıyor, 80 numaralı porta gelen istekleri de Nginx uygulamaya aktarıyor. Uygulama süreci zorla sonlandırılsa veya sunucu yeniden başlatılsa bile PM2'nin uygulamayı tekrar çalıştırdığını da doğruladık.

Aşağıdaki sonuç kutuları, normal kullanıcı deploy ile çalıştırılan komutların çıktısıdır. Alan adı yerine örnek değer example.com kullanıldı ve istekler sunucu içinden Host başlığı eklenerek gönderildi.

🎯 Önemli noktalar
🗂️ İstek akışı
📂 NginxTers proxy
📄 CertbotHTTPS sertifikası
📂 PM2Çalıştırma ve otomatik başlatma
📄 Node.js uygulaması
PM2 ile uygulamayı çalıştırma ve açılışta otomatik başlatma
Nginx, 80 numaralı porttaki istekleri uygulamaya aktarır
Ön koşullar
Sunucuİlk kurulumu tamamlanmış Ubuntu 24.04sudo yetkisine sahip normal kullanıcı hesabıAlan adı HTTPS için gerekli

1️⃣ Node.js kurulumu

Node.js, NodeSource deposu apt'ye eklenerek kurulur; sürüm olarak Kasım 2026 itibarıyla LTS olan Node.js 26'yı seçtik.

🔲 NodeSource setup_26.x

export DEBIAN_FRONTEND=noninteractive

Bu değeri önce tanımlarsanız paket kurulumu sırasında ayar soran pencere açılmaz. Aşağıdaki sudo komutunun -E seçeneği bu değeri root yetkisiyle çalışan komuta da aynen aktarır.

curl -fsSL https://deb.nodesource.com/setup_26.x | sudo -E bash -
2026-11-07 16:08:16 - Repository configured successfully.
2026-11-07 16:08:16 - To install Node.js, run: apt install nodejs -y

Betik depoyu eklemeyi bitirdiğinde Node.js'i diğer Ubuntu paketleri gibi apt ile kurabilirsiniz.

🔲 apt-get install nodejs

sudo -E apt-get install -y nodejs
Setting up libatomic1:amd64 (14.2.0-4ubuntu2~24.04.1) ...
Setting up nodejs (26.13.0-1nodesource1) ...
Processing triggers for libc-bin (2.39-0ubuntu8.9) ...
Processing triggers for man-db (2.12.0-4build2) ...

🔲 node -v, npm -v

node -v
v26.13.0
npm -v
11.20.0

npm, nodejs paketinin içinde geldiği için ayrıca kurmanız gerekmez.

2️⃣ Node.js test uygulaması

PM2 ile Nginx'in doğru bağlandığını görmek için, istek aldığında tek satır metin döndüren bir test uygulaması oluşturun.

🔲 app.js

mkdir -p ~/hello

Ev klasörünüzün altında hello klasörünü oluşturun ve içine app.js dosyasını yazın.

cat > ~/hello/app.js <<'APP'
const http = require('http');
http.createServer((req, res) => {
  console.log(req.method, req.url);
  res.end('hello from node\n');
}).listen(3000, '127.0.0.1');
APP

Uygulama yalnızca 127.0.0.1'in 3000 numaralı portunda istek kabul ettiği için sunucunun dışından doğrudan açılmaz. Dışarıdan gelen istekleri Nginx'in alıp bu porta aktarmasını ayarlayacağız.

3️⃣ PM2 ile uygulama çalıştırma ve otomatik başlatma

PM2, Node.js uygulamalarını arka planda çalıştıran ve uygulama süreci durduğunda onu yeniden çalıştıran bir süreç yönetim aracıdır. Sunucu açıldığında uygulamayı tekrar ayağa kaldıran otomatik başlatmayı da bu bölümde ayarlıyoruz.

🔲 npm install -g pm2

sudo npm install -g pm2
added 45 packages in 5s
pm2 -v
[PM2] Spawning PM2 daemon with pm2_home=/home/deploy/.pm2
[PM2] PM2 Successfully daemonized
7.1.2

pm2 komutu ilk kez çalıştırıldığında PM2 arka plan hizmeti de başlar; kurulan PM2 sürümü 7.1.2'dir.

🔲 pm2 start

cd ~/hello

app.js dosyasının bulunduğu klasöre geçin, ardından uygulamayı PM2 ile çalıştırın.

pm2 start app.js --name hello
[PM2] Starting /home/deploy/hello/app.js in fork_mode (1 instance)
[PM2] Done.
┌────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name     │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0  │ hello    │ default     │ N/A     │ fork    │ 17257    │ 0s     │ 0    │ online    │ 0%       │ 26.3mb   │ deploy   │ disabled │
└────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

--name, PM2 listesinde görünecek uygulama adıdır ve sonraki komutlar uygulamayı bu adla belirtir.

pm2 list
┌────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name     │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0  │ hello    │ default     │ N/A     │ fork    │ 17257    │ 2s     │ 0    │ online    │ 0%       │ 58.5mb   │ deploy   │ disabled │
└────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
host metrics | cpu: 0% | ram usage: 38.8% | disk: ⇓ 0mb/s ⇑ 0mb/s

status sütunundaki online uygulamanın çalıştığı anlamına gelir; ↺ sütunu ise PM2'nin uygulamayı kaç kez yeniden başlattığını gösterir.

🔲 curl 127.0.0.1:3000

Sunucu içinden istek gönderdiğinizde uygulamanın döndürdüğü tek satır metin olduğu gibi görünür.

curl -sS http://127.0.0.1:3000/
hello from node

🔲 pm2 restart

Uygulama kodunu değiştirdikten sonra değişikliği uygulamak için uygulamayı yeniden başlatın.

pm2 restart hello
Use --update-env to update environment variables
[PM2] Applying action restartProcessId on app [hello](ids: [ 0 ])
[PM2] [hello](0) ✓
┌────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name     │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0  │ hello    │ default     │ N/A     │ fork    │ 17306    │ 0s     │ 1    │ online    │ 0%       │ 19.3mb   │ deploy   │ disabled │
└────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
host metrics | cpu: 0% | ram usage: 38.8% | disk: ⇓ 0mb/s ⇑ 0mb/s
pm2 list
┌────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name     │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0  │ hello    │ default     │ N/A     │ fork    │ 17306    │ 2s     │ 1    │ online    │ 0%       │ 58.3mb   │ deploy   │ disabled │
└────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
host metrics | cpu: 0% | ram usage: 38.8% | disk: ⇓ 0mb/s ⇑ 0mb/s

Yeniden başlatmadan sonra pid 17257'den 17306'ya değişti ve ↺ sütunu 1'e çıktı.

🔲 kill -9

Uygulama sürecinin aniden durması durumunu kill -9 ile oluşturabilirsiniz; pm2 pid uygulamanın süreç numarasını yazdırır.

kill -9 "$(pm2 pid hello)"

3 saniye sonra PM2 listesini tekrar kontrol edin.

pm2 list
┌────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name     │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0  │ hello    │ default     │ N/A     │ fork    │ 17797    │ 3s     │ 2    │ online    │ 0%       │ 58.6mb   │ deploy   │ disabled │
└────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
host metrics | cpu: 51.2% | ram usage: 35.8% | eth0: ⇓ 1kb/s ⇑ 1kb/s | eth1: ⇓ 19kb/s ⇑ 0mb/s | disk: ⇓ 728kb/s ⇑ 3.1mb/s

PM2 uygulamayı yeni pid 17797 ile tekrar çalıştırmıştı, uygulama online durumdaydı ve ↺ sütunu 2'ye çıkmıştı.

🔲 pm2 save

pm2 save
[PM2] Saving current process list...
[PM2] Successfully saved in /home/deploy/.pm2/dump.pm2

pm2 save şu anda çalışan uygulamaların listesini dump.pm2 dosyasına kaydeder; açılışta PM2 uygulamaları bu listeye göre tekrar çalıştırır.

🔲 pm2 startup

PM2'nin açılışta çalışması için PM2'yi systemd hizmeti olarak kaydedin. Aşağıdaki komut, şu anki hesap adını ve ev klasörünü kullanarak hizmeti oluşturur.

sudo env PATH="$PATH" pm2 startup systemd -u "$(id -un)" --hp "$HOME"
ExecStart=/usr/lib/node_modules/pm2/bin/pm2 resurrect
ExecReload=/usr/lib/node_modules/pm2/bin/pm2 reload all
ExecStop=/usr/lib/node_modules/pm2/bin/pm2 kill

[Install]
WantedBy=multi-user.target

Target path
/etc/systemd/system/pm2-deploy.service
Command list
[ 'systemctl enable pm2-deploy' ]
[PM2] Writing init configuration in /etc/systemd/system/pm2-deploy.service
[PM2] Making script booting at startup...
[PM2] [-] Executing: systemctl enable pm2-deploy...
Created symlink /etc/systemd/system/multi-user.target.wants/pm2-deploy.service → /etc/systemd/system/pm2-deploy.service.
[PM2] [v] Command successfully executed.

env PATH, root yetkisiyle çalışan pm2'nin şu anki hesabın node yolunu bulmasını sağlayan kısımdır; -u ve --hp ise hizmetin çalışacağı hesap ve ev klasörüdür. Hizmet dosyasındaki ExecStart satırı pm2 resurrect'tir ve açılışta kaydedilmiş uygulama listesini geri yükler.

systemctl status pm2-deploy --no-pager | head -n 5
○ pm2-deploy.service - PM2 process manager
     Loaded: loaded (/etc/systemd/system/pm2-deploy.service; enabled; preset: enabled)
     Active: inactive (dead)
       Docs: https://pm2.keymetrics.io/

enabled, hizmetin açılışta çalışacağı anlamına gelir. pm2 startup hizmeti yalnızca kaydeder, şimdi çalıştırmaz; bu yüzden inactive (dead) görünür ve hizmet bir sonraki açılıştan itibaren çalışır.

🔲 Yeniden başlatma sonrası pm2 list

sudo systemctl reboot

Yaklaşık 2 dakika sonra deploy ile tekrar bağlanın ve uygulamanın durumunu kontrol edin.

uptime -p
up 2 minutes
pm2 list
┌────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name     │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0  │ hello    │ default     │ N/A     │ fork    │ 976      │ 108s   │ 0    │ online    │ 0%       │ 63.0mb   │ deploy   │ disabled │
└────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
host metrics | cpu: 0.3% | ram usage: 35.1% | disk: ⇓ 4kb/s ⇑ 4kb/s

Sunucu açıldıktan 2 dakika sonra uygulama zaten 108 saniyedir online durumdaydı; PM2 uygulamayı açılışla birlikte tekrar çalıştırmıştı.

4️⃣ Nginx ters proxy ayarı

Uygulama yalnızca 127.0.0.1:3000 üzerinde istek kabul ettiği için 80 numaralı porta gelen istekleri Nginx alır ve uygulamaya aktarır. İstekleri önde karşılayıp arkadaki sunucuya ileten bu yapıya ters proxy denir.

🔲 apt-get install nginx

sudo -E apt-get install -y nginx
Setting up nginx-common (1.24.0-2ubuntu7.19) ...
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service.
Setting up nginx (1.24.0-2ubuntu7.19) ...
nginx -v
nginx version: nginx/1.24.0 (Ubuntu)

Ubuntu 24.04 deposundaki Nginx 1.24.0 sürümüdür ve kurulumla birlikte açılışta çalışacak şekilde kaydedilir.

🔲 /etc/nginx/sites-available/hello

/etc/nginx/sites-available/hello dosyasını oluşturun ve içine aşağıdaki içeriği yazın.

server {
    listen 80;
    server_name 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;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

proxy_pass, isteklerin aktarılacağı uygulama adresidir. Host, X-Real-IP, X-Forwarded-For ve X-Forwarded-Proto başlıkları, uygulamanın asıl isteğin alan adını, ziyaretçinin IP'sini ve isteğin http mi https mi olduğunu bilebilmesi için aktarılan değerlerdir.

proxy_http_version 1.1 ile Upgrade ve Connection başlıkları, WebSocket bağlantılarını uygulamaya kadar iletmek için gerekir. Upgrade ve Connection başlıkları bir proxy'den geçtikten sonra uygulamaya iletilmediği için ayarlarda doğrudan eklenir.

sudo ln -sf /etc/nginx/sites-available/hello /etc/nginx/sites-enabled/hello

Nginx yalnızca sites-enabled klasöründe bağlantısı oluşturulan ayarları okur.

🔲 nginx -t

sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
sudo systemctl reload nginx

Önce nginx -t ile ayar söz dizimini kontrol edin; geçerse reload ile Nginx'i durdurmadan yeni ayarı uygulayın.

🔲 Host başlıklı curl isteği

curl -sS -i -H 'Host: example.com' http://127.0.0.1/ | head -n 4
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Sat, 07 Nov 2026 08:09:38 GMT
Content-Length: 16

Ayar yeniden okunduktan sonra Host başlığıyla gönderilen isteğe uygulamanın yanıtı döndü. Yanıt başlıklarındaki Server satırı nginx'tir; Content-Length 16 ise uygulamanın döndürdüğü hello from node satırıyla (satır sonu dahil 16 bayt) aynı boyuttadır. Sunucunun dışından bağlanmak için güvenlik duvarında 80 numaralı portun açık olması gerekir.

🧭 Node.js uygulamasına istek ve yeniden çalıştırma
Açıklama diyagramı
🧭 Durum
İstek akışı
🌐Tarayıcıexample.com isteği
➜
🧱Nginx80 numaralı port, proxy_pass
➜
🟢Node.js uygulaması127.0.0.1:3000
Süreç yönetimi
⚙️systemdpm2-deploy hizmeti
➜
🔁PM2dump.pm2 uygulama listesi
➜
🟢Node.js uygulamasıhello
Tarayıcı isteğini 80 numaralı porttaki Nginx alır, proxy_pass'te yazan 127.0.0.1:3000 adresine aktarır ve uygulamanın yanıtını olduğu gibi döndürür.
Uygulama süreci durduğunda PM2 aynı uygulamayı hemen tekrar çalıştırır. kill -9 ile durdurup 3 saniye sonra pm2 list'e bakıldığında uygulama yeni pid ile online durumdaydı.
Sunucu açıldığında systemd pm2-deploy hizmetini çalıştırır; PM2 de pm2 save ile kaydedilen listeye göre uygulamaları tekrar çalıştırır.

5️⃣ Certbot ile HTTPS uygulama

Certbot'un Nginx eklentisiyle sertifika almayı ve Nginx ayarını değiştirmeyi tek seferde yapabilirsiniz. Alan adı sunucunun IP'sini gösterirken aşağıdaki komutu çalıştırın.

sudo certbot --nginx

Certbot kurulumu ve otomatik yenileme testi aşağıdaki yazıda anlatılıyor; o yazıdaki sertifika alma komutu Apache içindir, bu yüzden yalnızca sertifika alma adımını yukarıdaki komutla değiştirerek çalıştırın.

🔢 SSS & önerilen içerikler

Nginx, proxy_pass'te yazan uygulama adresine bağlanamazsa 502 Bad Gateway döndürür. pm2 stop hello ile uygulama durdurulduktan sonra aynı istek 502 verdi ve /var/log/nginx/error.log dosyasında [connect() failed (111: Connection refused) while connecting to upstream] satırı ile bağlanılmaya çalışılan adres http://127.0.0.1:3000/ kaldı. Önce pm2 list'te uygulamanın online olup olmadığını, uygulamanın istek kabul ettiği portun proxy_pass adresiyle aynı olup olmadığını kontrol edin. Uygulama yeniden başlatıldıktan sonra aynı istek 200 ile döndü.

PM2, uygulamanın çıktısını, onu çalıştıran hesabın ev klasörü altındaki .pm2/logs klasöründe uygulama adına göre dosyalara kaydeder. pm2 logs hello --nostream --lines 5 çalıştırıldığında /home/deploy/.pm2/logs/hello-out.log ve hello-error.log dosyalarının son satırları göründü; out dosyasında uygulamanın kaydettiği GET / isteği vardı. --nostream olmadan çalıştırırsanız yeni gelen günlükleri sürekli yazdırmaya devam eder.

Otomatik başlatma hizmeti pm2 unstartup systemd ile silinir. Uygulama ekledikten veya kaldırdıktan sonra açılışta değişen listeye göre çalışması için pm2 save'i yeniden çalıştırın. pm2 startup çalıştırıldığında da çıktının sonunda bu iki komut birlikte gösterildi.

ℹ️ Satış ortaklığı bildirimi
Bu sitenin içeriklerinde satış ortaklığı bağlantıları bulunur. Bir ziyaretçi bu bağlantılar üzerinden ürün veya hizmet satın alırsa bu site satıcıdan komisyon alır. Bu süreçte alıcının ödediği tutar (kampanya indirimlerinde tutar düşer↓) artmaz. Yayımlanan fiyat, indirim ve stok bilgileri yazının yazıldığı zamana aittir ve gerçek durumdan farklı olabilir; bu yüzden satın almadan önce son bilgiyi satıcıdan kontrol etmenizi rica ederiz. Ürünler kendi ölçütlerimize göre seçilir ve değerlendirilir; komisyon alınıp alınmaması tanıtım sırasını veya değerlendirme içeriğini etkilemez.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

İnsan olduğunuzu kanıtlayın: 8   +   8   =