---
title: "Nginx + PHP-FPM Pool Ayrımı"
description: "Aynı VPS üzerinde birden fazla PHP uygulaması çalıştırırken, tek bir PHP-FPM pool yerine uygulama başına ayrı pool tanımlamanın somut faydaları."
url: https://sade.dev/tr/notes/nginx-php-fpm-pool-ayrimi/
lang: tr
author: "Muhammet Şafak"
published: 2026-04-26
updated: 2026-09-06
section: Note
tags: ["nginx","php-fpm","production","vps"]
summary: "Uygulama başına ayrı bir PHP-FPM pool'u iki şeyi ayırır: dosya sistemi erişimini ve worker bütçesini. Ayırmadığı şey opcache ve reload — pool'lar tek bir master'ı paylaşır ve o master'ın tek yeniden yükleme sinyali bütün worker'ları birlikte götürür. İzolasyon çalışma zamanında geçerli, deploy yolunda değil. Hangisini satın aldığınızı bilerek kurun."
---

# Nginx + PHP-FPM Pool Ayrımı

> Uygulama başına ayrı bir PHP-FPM pool'u iki şeyi ayırır: dosya sistemi erişimini ve worker bütçesini. Ayırmadığı şey opcache ve reload — pool'lar tek bir master'ı paylaşır ve o master'ın tek yeniden yükleme sinyali bütün worker'ları birlikte götürür. İzolasyon çalışma zamanında geçerli, deploy yolunda değil. Hangisini satın aldığınızı bilerek kurun.

VPS üzerinde iki, üç, beş PHP uygulaması çalıştırmak — küçük SaaS dünyasında çok normal. Default kurulumda hepsi tek bir `www.conf` pool'unu paylaşıyor. Bu çoğu zaman çalışıyor — çalışmayana kadar.

## Tek pool'da ne ters gider?

Tek pool şu anlama geliyor:

1. Bir uygulama `pm.max_children = 50`'yi tüketirse diğerleri response üretemez.
2. Bir uygulamadaki memory leak diğerlerinin worker'larını da OOM'a sürükler.
3. Bir uygulamayı yeniden başlatmak (örn. opcache reset) hepsinin worker'larını yeniden başlatır.

Pool ayrımı ilk iki sorunu çözüyor. Üçüncüsünü çözmüyor: PHP-FPM'de pool başına reload yok — tek yeniden yükleme sinyali `SIGUSR2`, master altındaki tüm worker'ları yeniden yüklüyor.

## Pool başına config

`/etc/php/8.4/fpm/pool.d/billing.conf`:

```ini
[billing]
user = billing
group = billing
listen = /run/php/billing.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 8
pm.max_requests = 500

request_terminate_timeout = 30s
catch_workers_output = yes
decorate_workers_output = no
clear_env = no

php_admin_value[memory_limit] = 256M
php_admin_value[error_log] = /var/log/php-fpm/billing-error.log
```

`/etc/php/8.4/fpm/pool.d/notify.conf`:

```ini
[notify]
user = notify
group = notify
listen = /run/php/notify.sock
; ...
pm.max_children = 8       ; daha hafif uygulama
php_admin_value[memory_limit] = 128M
```

İki şey kazandık:

- **Filesystem izolasyonu** — `user = billing` ile billing uygulaması notify'ın dosyalarına yazamaz.
- **Resource izolasyonu** — billing'in `pm.max_children`'i tükenirse notify hâlâ çalışır.

## Nginx tarafında

Her vhost kendi socket'ine bağlanıyor:

```nginx
server {
    server_name billing.example.com;
    root /var/www/billing/public;

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/billing.sock;
        # standart fastcgi params
    }
}

server {
    server_name notify.example.com;
    root /var/www/notify/public;

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/notify.sock;
    }
}
```

## opcache shared mı, ayrı mı?

PHP-FPM master process'i opcache'i paylaşıyor — bu yüzden pool'lar arasında opcache otomatik izole değildir. `opcache.validate_root = 1` bunu değiştirmiyor: bu direktif chroot'lu ortamlardaki ad çakışmalarını önlemek için `/` dizininin inode'unu önbellek anahtarına katıyor, yani ayırt ettiği şey chroot kökleri, metindeki Nginx `root` path'leri değil — ve buradaki pool'larda `chroot` yok. Pratikte: aynı master, ayrı pool'lar yeterli.

## İzleme

`/status` endpoint'ini pool'a açın:

```ini
pm.status_path = /status
```

Sonra Nginx tarafında bunu sadece localhost'a açın:

```nginx
location ~ ^/status$ {
    access_log off;
    allow 127.0.0.1;
    deny all;
    fastcgi_pass unix:/run/php/billing.sock;
    # ...
}
```

`curl localhost/status` ile aktif worker, kuyruk uzunluğu, slow request sayısı gibi metrikleri alıp Prometheus exporter veya basit bir cron'la izleyebilirsiniz.

## Ne zaman tek pool yeterli?

Aynı kod tabanının iki kopyasını çalıştırıyorsanız (örn. `app.example.com` ve `staging.example.com`) ayrı pool gereksiz. Farklı domain'ler ama aynı süreç sınıfı. Ayrım, uygulamaları **operasyonel olarak bağımsız** kılmak istediğinizde değerli.

## Sık sorulanlar

**Pool ayrımı uygulamalar arasında opcache'i izole eder mi?**

Hayır. Pool'lar tek bir PHP-FPM master'ı altında çalışır ve onun opcache'ini paylaşır. opcache.validate_root da bunu değiştirmez: o direktif chroot'lu ortamlardaki ad çakışmalarını önlemek için vardır ve buradaki pool'larda chroot yoktur. Ayrı pool, dosya sistemi ve kaynak izolasyonu getirir; ayrı bir opcode cache değil.

**Tek bir PHP-FPM pool tek başına reload edilebilir mi?**

Hayır. FPM'de tek bir yeniden yükleme sinyali var, SIGUSR2, ve o da tüm worker'ları graceful biçimde yeniden yükleyip FPM yapılandırmasını baştan okuyor. Pool başına bir sinyal yok; bu yüzden bir uygulamayı yeniden başlatmak makinedeki diğer bütün pool'ları da geri döndürüyor.

**Ne zaman tek pool yeterli olur?**

Uygulamalar aynı süreç sınıfındaysa — aynı kod tabanının iki kopyası, örneğin farklı domain'lerde production ve staging. Ayrım, uygulamaların birbirinden operasyonel olarak bağımsız olmasını istediğinizde değerli.


## Kaynaklar

- [FPM configuration: per-pool user, listen, pm.max_children and pm.status_path](https://www.php.net/manual/en/install.fpm.configuration.php) — PHP
- [php-fpm(8): SIGUSR2 is a graceful reload of all workers plus a reload of the FPM config](https://github.com/php/php-src/blob/master/sapi/fpm/php-fpm.8.in) — PHP
- [OPcache configuration: opcache.validate_root prevents name collisions in chrooted environments](https://www.php.net/manual/en/opcache.configuration.php) — PHP
- [FPM status page: active processes, listen queue and slow requests](https://www.php.net/manual/en/fpm.status.php) — PHP
