# DeployerPHP zero-downtime deploy'da OPcache'ten gelen 500 hatalarını nasıl engellerim?

> Symlink'i çevirdikten sonra PHP-FPM'i graceful reload edin (restart değil); Octane'deyseniz worker'lar için octane:reload şart, opcache_reset isteğe bağlı.

- Soruldu: 2026-06-10
- Yanıtlandı: 2026-06-12
- Soran: Şükrü
- Etiketler: ci-cd, laravel
- Kaynak: https://www.muhammetsafak.com.tr/sor-bakalim/deployerphp-ile-sifir-kesinti-deploy-ve-opcache-temizligi/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
**Soru:** DeployerPHP ile deploy ediyoruz: `releases/X` oluşuyor, `composer install` çalışıyor, asset'ler derleniyor, en son `current` symlink yeni klasöre dönüyor. Bu symlink değişiminde PHP-FPM'in OPcache'inde eski kodlar kaldığı için (cache poisoning) 500 hataları çıkıyor.

Pipeline'a `php-fpm reload` ve OPcache temizliğini güvenli şekilde nasıl entegre ederim?


Kısa cevap: Symlink çevirmek atomik ama yeterli değil — flip'ten sonra PHP-FPM'i graceful **reload** etmeniz lazım; OPcache'i hiçbir release'ten sonra eski koda yapışık bırakmayın.

Yaşadığınız şey klasik **OPcache cache poisoning**: OPcache, derlenmiş bytecode'u dosya yoluna göre keyler. `current` symlink'inin kendi yolu hiç değişmediği için worker'lar bir önceki release'in bytecode'unu sunmaya devam eder; bazı worker'lar eski, bazıları yeni realpath'i görünce dosya yolları karışır ve 500 patlar.

1. **En temiz yol: symlink'ten sonra PHP-FPM'i graceful reload edin.** `kill -USR2 <fpm-master>` ya da `systemctl reload php-fpm` — bu, FPM'in yeni `realpath`'i görmesini sağlar ve OPcache'i tazeler. **Reload, restart değil:** mevcut istekler tamamlanır, yeni worker'lar yeni kodu derler, kesinti olmaz. Deployer'da bunu `deploy:symlink` adımına hook'layın.
2. **Alternatif: `opcache_reset()`'i deploy hook'undan çağırın.** FPM soketine `cachetool` ile bağlanıp cache'i resetlemek, web'den bir reset endpoint'i tetiklemekten daha temiz — CLI ve FPM ayrı OPcache tutar, yani `php artisan` ile resetlemek FPM'i etkilemez. Reload bunu zaten kapsadığı için çoğu zaman gerek kalmaz.
3. **`opcache.revalidate_freq`'i ayarlamak tek başına çözüm değil.** Aynı dosya yolu değiştiğinde işe yarar; ama release'ler arası yol farkında revalidate sizi kurtarmaz. Release sınırında garantili olan reset/reload'dur.
4. **Octane bambaşka bir hikaye.** Octane'de kod diskten değil **bellekten** sunulur; uzun ömürlü worker'lar eski sınıfları RAM'da tutar. Symlink flip'ten sonra `php artisan octane:reload` ile worker'ları yenilemeniz şart, yoksa OPcache'i ne yaparsanız yapın eski kod ayakta kalır.

**Sonuç:** Akış net olsun — symlink'i çevirin → PHP-FPM'i graceful reload edin (Octane'deyseniz `octane:reload`) → isterseniz `opcache_reset`. Bunu Deployer'da symlink adımına bağlayın ve bir release'ten sonra OPcache'i asla eski koda yapışık bırakmayın. Octane'in bellekte kod tutmasının deploy tarafındaki bedeli ayrı bir hub yazısının konusu; oradaki kalıcı-süreç modelini iyi tanırsanız bu reload adımını atlamazsınız.

## İlgili Yazılar

- [Laravel Octane: kalıcı süreçle gelen performans](/blog/laravel-octane-kalici-surecle-gelen-performans/) — Blog
- [CI/CD'de veritabanı göçlerini (migrations) güvenli nasıl çalıştırırım?](https://www.muhammetsafak.com.tr/sor-bakalim/ci-cd-de-veritabani-goclerini-guvenli-calistirmak/) — Sor Bakalım
- [İşlediğim satırları aynı anda güncellerken chunk yerine chunkById mı kullanmalıyım?](https://www.muhammetsafak.com.tr/sor-bakalim/isledigim-satirlari-ayni-anda-guncellerken-chunk-yerine-chunkbyid-mi-kullanmaliyim/) — Sor Bakalım
- [Container'ı non-root çalıştırınca mount edilen storage dizinine yazamıyorum, izinleri nasıl çözerim?](https://www.muhammetsafak.com.tr/sor-bakalim/containeri-non-root-calistirinca-mount-edilen-storage-dizinine-yazamiyorum-izinleri-nasil/) — Sor Bakalım
