# Laravel Projesini VPS'e Çıkarmak: Docker ve Nginx ile Sıfırdan Yayına

Canonical: https://kaantanis.com/bloglar/docker/laravel-projesini-vpse-cikarmak-docker-ve-nginx-ile-sifirdan-yayina

Yayın tarihi: 2026-09-21

Etiketler: Docker, Genel, Laravel

Yazar: Kaan Tanış

Localhost'ta her proje çalışır. Hata sayfası bile güzel görünür, log kimseye batmaz, kuyruklar beklemez. Sonra site yayına çıkar ve gerçek dünya başlar: farklı PHP sürümü, unutulmuş bir .env satırı, çalışmayan bir worker. Dikkat ettin mi, bütün deploy faciaları nedense cuma akşamı olur.Ben küçük ve orta boy Laravel işlerinde aynı kalıbı kullanıyorum: Docker ile ortamı sabitlemek, önüne Nginx koymak, yayına çıkarken de kısa bir kontrol listesinden geçmek. Abartı bir altyapı değil. Ama "benim makinemde çalışıyordu" cümlesini tarihe gömüyor."Benim makinemde çalışıyordu" cümlesiSorun genelde kodda değil, ortam farkında. Sende PHP 8.3 var, sunucuda 8.1. Sende Redis çalışıyor, sunucuda yok. Sende queue worker terminalde açık, sunucuda kimse çalıştırmamış. Docker bu farkı kapatıyor: uygulama, veritabanı ve önbellek aynı tarifle her yerde ayağa kalkıyor.Tek bir compose dosyası yetiyor. Aşağısı kabaca benim kullandığım iskelet:services:
 app:
 build: ./docker/php
 volumes:
 - ./:/var/www/html
 depends_on:
 - db
 - redis

 web:
 image: nginx:stable
 ports:
 - "80:80"
 volumes:
 - ./:/var/www/html
 - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
 depends_on:
 - app

 db:
 image: mysql:8.0
 environment:
 MYSQL_DATABASE: site
 MYSQL_USER: site
 MYSQL_PASSWORD: ${DB_PASSWORD}
 volumes:
 - db-data:/var/lib/mysql

 redis:
 image: redis:alpine

volumes:
 db-data:Buradaki tek kritik nokta veri. Kod her deployda yenilenir, veritabanı yenilenmez. O yüzden db için volume şart. Volumesiz MySQL ile yayına çıkanın bir container restartına bakar her şeyi kaybetmesi.Nginx tarafı: trafiği karşılayan kapıNginx bu kurulumda PHP çalıştırmıyor, sadece kapıda durup istekleri PHP-FPM'e iletiyor. Laravel için gereken config kısa:server {
 listen 80;
 root /var/www/html/public;
 index index.php;

 location / {
 try_files $uri $uri/ /index.php?$query_string;
 }

 location ~ \.php$ {
 fastcgi_pass app:9000;
 fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
 include fastcgi_params;
 }
}İki satır genelde sorun çıkarır. Biri root: mutlaka public klasörünü göstermeli, proje kökünü değil. Kökü gösteren config .env dosyanızı internete açar. Diğeri fastcgi_pass içindeki servis adı: compose'daki PHP servisinin adıyla aynı olmalı. İsim tutmazsa Nginx 502 verir ve loga bakmadan anlaşılmaz.Yayına çıkarken unutulanlarOrtam hazır, kod atıldı, site açılıyor. İşte asıl liste burada başlıyor. Bunların yarısı unutulunca site yine açılır, o yüzden sinsi. Hata haftalar sonra patlar.Önce .env tarafı: APP_ENV production olmalı, APP_DEBUG kesinlikle false. Debug açık kalan production site, hata sayfasında veritabanı şifresini ziyaretçiye ikram eder. Bunu bir kez canlıda gören bir daha unutmaz.Sonra cache ve migration:php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan migrate --force
php artisan storage:linkmigrate komutundaki --force bayrağı production'da onay sorusunu geçiyor. CI ile deploy ediyorsanız şart, yoksa pipeline ilk migrationda takılıp kalıyor.Kuyruk tarafı ayrı bir konu. Queue kullanan ama worker çalıştırmayan proje gördüm, mailler saatlerce gitmemiş. Worker ya supervisord ile ya da restart politikası olan ayrı bir container ile yaşamalı. Ölünce kendiliğinden dirilmeyen worker, worker değildir.Zamanlanmış işler için de sunucuda tek satırlık cron olmalı:* * * * * cd /var/www/html && php artisan schedule:run >> /dev/null 2>&1SSL tarafını sona bıraktım çünkü en kolayı o. Certbot tek komutla sertifikayı alıp Nginx configine işliyor. 2026'da HTTPS'siz site açmak hem güven hem sıralama tarafında kendi ayağına sıkmak.Deploy checklist sadece teknik bir liste değil. Gece rahat uyuma listesi. Her maddeyi işaretlediysen telefon çaldığında paniklemiyorsun.Her projeye Docker gerekmezBunu da açık söyleyeyim: tek sayfalık tanıtım sitesi, ayda birkaç kez güncellenen küçük bir kurumsal site için bu kurulum fazla. Paylaşımlı hosting ya da basit bir panel yeterliyse oradan çıkmak marifet değil, masraf. Docker'ın karşılığını verdiği yer belli: Laravel gibi bağımlılığı olan, kuyruk, zamanlanmış iş, önbellek barındıran projeler.Ölçüm basit. Projeyi başkasının makinesinde sıfırdan ayağa kaldırmak yarım saatten uzun sürüyorsa ortamı sabitlemeye değer. Sürmüyorsa mevcut düzenle devam.Yayın öncesi kontrol listesiAPP_ENV production, APP_DEBUG false, APP_KEY üretilmiş.config, route ve view cache alınmış.Migration --force ile çalışmış, storage linki duruyor.Queue worker supervisord ya da restart politikalı container ile yaşıyor.Scheduler cronu sunucuda kayıtlı.SSL aktif, .env ve storage dışarıdan erişilemiyor.Veritabanı yedeği otomatik, nereye gittiği belli.Bu liste uzun görünüyor ama ikinci deploydan sonra kas hafızasına dönüyor. İlki biraz terletiyor, sonrası rutin. Tıpkı iyi yazılmış kod gibi: ilk kurulum zahmetli, sonrası sessiz.
