Docker & DevOps

    Dev, Staging ve Prod Ortam Ayrımı

    Üç ortamın sorumlulukları, yapılandırma yönetimi ve güvenli terfi akışının kurulumu.

    10 dk okuma Güncellendi: 25 Ağustos 2026

    Küçük bir projede genelde tek bir sunucu vardır ve herkes doğrudan oraya yükler. İlk birkaç ay sorunsuz geçer, sonra bir cuma akşamı yapılan küçük bir değişiklik ödeme akışını bozar ve geri dönmenin yolu olmadığı fark edilir. Dev, staging ve prod ortam ayrımı, bu anı yaşamamak için kurulan en temel disiplindir: değişikliğin canlıya ulaşmadan önce en az bir kez, canlıya benzeyen bir yerde denenmesi.

    Bu rehberde üç ortamın gerçekte hangi soruyu cevapladığını, staging'in neden "ikinci bir test sunucusu" olmadığını, yapılandırmayı koddan nasıl ayıracağını ve veri ile gizli bilgileri ortamlar arasında nasıl yöneteceğini anlatacağım. Sonra Docker Compose ile üç ortamı tek kod tabanından türetmeyi, terfi (promotion) akışını ve bu kurulumda en sık düşülen tuzakları — özellikle "staging'de çalışıyordu" klasiğini — konuşacağız.

    Her Ortam Hangi Soruyu Cevaplar#

    Ortamları isimleriyle değil, sorumluluklarıyla düşünmek gerekir. Üçü de aynı kodu çalıştırır ama farklı soruların cevabını verir ve bu ayrım netleşmediğinde staging bir işe yaramaz.

    Dev (geliştirme) ortamı "bu kod derleniyor ve temel akış çalışıyor mu" sorusunu cevaplar. Geliştiricinin kendi makinesinde ya da paylaşılan bir geliştirme sunucusunda çalışır; veri sahtedir, hız her şeyden önemlidir, bozulması normaldir. Burada gerçek para, gerçek e-posta ve gerçek müşteri verisi olmaz.

    Staging (hazırlık) ortamı "bu değişiklik canlıda da çalışacak mı" sorusunu cevaplar. Bu yüzden staging'in tek anlamlı tanımı vardır: canlı ortamın mümkün olduğunca birebir kopyası. Aynı işletim sistemi, aynı veritabanı sürümü, aynı ters proxy yapılandırması, aynı ortam değişkeni isimleri. Farklı olan yalnızca değerler ve veri kümesinin ölçeğidir. Staging canlıdan farklı bir PHP sürümü çalıştırıyorsa, orada geçen bir test canlı hakkında hiçbir şey söylemez.

    Prod (canlı) ortamı "gerçek kullanıcılar gerçek işlerini yapabiliyor mu" sorusunu cevaplar. Buraya yalnızca staging'den geçmiş, değiştirilmemiş artefakt gider.

    KonuDevStagingProd
    VeriSahte / tohum veriAnonimleştirilmiş kopyaGerçek
    Dış servislerSahte (mock)Sağlayıcının test moduGerçek
    Hata gösterimiAyrıntılı, ekrandaKapalı, loglanırKapalı, loglanır
    ErişimHerkesIP kısıtlı / parolaGenel
    Arama motoruKapalıKapalı (noindex)Açık
    YedekGerekmezHaftalık yeterGünlük ve doğrulanmış
    E-postaEkrana / dosyayaYalnız iç adreslereGerçek alıcılara

    Bu tablodaki "e-posta" satırı hafife alınmamalı: staging'de yanlışlıkla gerçek adreslere yollanan bir toplu bildirim, hem itibar hem de gönderim puanı açısından pahalıya patlar. Staging'de e-postayı ya bir yakalayıcıya yönlendir ya da alıcı listesini bir iç alan adıyla sınırla. Toplu gönderim yapan bir sisteminiz varsa e-posta pazarlama tarafındaki liste hijyeni kuralları burada da geçerlidir.

    Aynı Kod, Farklı Yapılandırma#

    Ortam ayrımının teknik özü tek bir ilkeye dayanır: kod ortamlara göre değişmez, yapılandırma değişir. Kodun içinde if ($ortam === 'production') bloğu ne kadar çoksa, staging'in doğrulama gücü o kadar azalır — çünkü canlıda çalışan kod yolu staging'de hiç çalışmamış olur.

    Doğru yaklaşım, ortama bağlı her şeyi ortam değişkenine taşımaktır:

    # .env.example — sürüm kontrolüne giren, DEĞERSİZ şablon
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://firmaniz.com
    
    DB_HOST=127.0.0.1
    DB_PORT=5432
    DB_NAME=uygulama
    DB_USER=uygulama
    DB_PASSWORD=
    
    REDIS_URL=redis://127.0.0.1:6379/0
    MAIL_DRIVER=smtp
    MAIL_HOST=smtp.firmaniz.com
    LOG_LEVEL=warning
    

    Gerçek değerleri taşıyan .env dosyası asla sürüm kontrolüne girmez; yalnızca .env.example girer ve o da boş değerlerle. Bu ayrım kağıt üzerinde herkesin bildiği ama pratikte en sık ihlal edilen kuraldır; sonuçlarını .git klasörü ve .env dosyası ifşası yazısında ayrıntılı anlattım.

    Sunucudaki dosyanın izinlerini de daraltmayı unutma:

    # Sadece uygulama kullanıcısı okuyabilsin
    sudo chown root:uygulama /etc/uygulama/env
    sudo chmod 0640 /etc/uygulama/env
    
    # Git'e girmediğinden emin ol
    grep -q '^\.env$' .gitignore || echo '.env' >> .gitignore
    

    Her ortam için ayrı parola üretmek de temel bir kuraldır: staging veritabanı parolası canlıyla aynıysa, staging'e erişen herkes canlıya da erişebilir demektir. Güçlü ve birbirinden bağımsız değerler için şifre üretici aracımızı kullanabilirsin.

    Docker Compose ile Üç Ortamı Tek Tabanda Kurmak#

    Compose, ortak tanımı bir dosyada tutup ortama özel farkları üst katman dosyalarıyla eklemene izin verir. Bu, "aynı kod, farklı yapılandırma" ilkesinin en temiz uygulamasıdır.

    # compose.yml — TÜM ortamlarda ortak olan taban
    services:
      api:
        image: registry.firmaniz.com/siparis-api:${SURUM:-latest}
        env_file: [.env]
        depends_on:
          db:
            condition: service_healthy
        restart: unless-stopped
    
      db:
        image: postgres:16-alpine
        environment:
          POSTGRES_DB: ${DB_NAME}
          POSTGRES_USER: ${DB_USER}
          POSTGRES_PASSWORD: ${DB_PASSWORD}
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
          interval: 10s
          retries: 5
        volumes:
          - db-data:/var/lib/postgresql/data
    
    volumes:
      db-data:
    
    # compose.dev.yml — yalnızca geliştirme farkları
    services:
      api:
        build: .                    # imaj çekmek yerine yerelde derle
        command: npm run dev        # canlı yeniden yükleme
        environment:
          APP_DEBUG: "true"
          LOG_LEVEL: debug
        volumes:
          - .:/app                  # kodu bağla, her değişiklik anında yansısın
          - /app/node_modules
        ports:
          - "3000:3000"
    
    # compose.prod.yml — canlı farkları
    services:
      api:
        environment:
          APP_DEBUG: "false"
          LOG_LEVEL: warning
        deploy:
          resources:
            limits:
              memory: 1G
        ports:
          - "127.0.0.1:3000:3000"   # yalnız ters proxy erişsin
        logging:
          driver: json-file
          options: { max-size: "20m", max-file: "5" }
    

    Kullanımı tek satırdır:

    # Geliştirme
    docker compose -f compose.yml -f compose.dev.yml up
    
    # Canlı
    docker compose -f compose.yml -f compose.prod.yml up -d
    

    Buradaki port satırı önemli bir güvenlik detayı taşır: canlıda 127.0.0.1:3000:3000 yazarsan uygulama yalnızca yerel arayüzden erişilebilir olur ve dışarıya yalnız ters proxy bakar. 3000:3000 yazarsan uygulama tüm dünyaya açılır ve güvenlik duvarını atlamış olursun. Compose dosyalarının yapısına ve katmanlanmasına yabancıysan Docker Compose kullanımı yazısı temeli veriyor. Uzun komutları ezberlememek için de bu üç satırı bir Makefile hedefine bağlamak en pratik çözümdür.

    Veri ve Gizli Bilgiler: En Kritik Ayrım#

    Ortam ayrımının en çok ihlal edilen yeri veridir. Canlı veritabanını olduğu gibi staging'e kopyalamak yaygın ve kolaydır ama KVKK açısından ciddi bir sorundur: staging genelde daha gevşek erişim kurallarına sahiptir ve o kopyada gerçek müşteri isimleri, telefonlar ve adresler bulunur.

    Doğru yol, kopyayı anonimleştirerek almaktır:

    -- Staging'e aktarmadan önce kişisel verileri maskele
    UPDATE musteriler SET
      ad        = 'Test',
      soyad     = 'Kullanici' || id,
      eposta    = 'test+' || id || '@ornek.com',
      telefon   = '0555000' || LPAD(id::text, 4, '0'),
      adres     = 'Ornek Mahallesi No:1';
    
    -- Ödeme kartı ve kimlik verisi hiç taşınmamalı
    TRUNCATE TABLE odeme_yontemleri;
    TRUNCATE TABLE kimlik_belgeleri;
    

    İkinci kritik nokta, staging'in dış servislerle nasıl konuştuğudur. Ödeme sağlayıcısının test anahtarları, SMS sağlayıcısının sandbox modu ve e-posta için bir yakalayıcı kullanılmalıdır. Bir kez bile canlı ödeme anahtarını staging'e koyarsan, orada çalışan bir test betiği gerçek para hareketi yaratabilir.

    Üçüncüsü yedeklemedir ve ortamlara göre farklı sertliktedir. Canlı yedeği günlük alınmalı, geri yükleme testi yapılmalıdır; hiç denenmemiş bir yedek, yedek değildir. Staging'de yedek almak yerine "canlıdan yeniden üret" yaklaşımı genelde daha pratiktir. Yedek stratejinizi kurmak için yedekleme çözümlerimize bakabilirsiniz.

    Terfi Akışı: Aynı Artefakt Yukarı Çıkar#

    Ortam ayrımının işe yaraması için tek bir kural yeter: staging'de test edilen şey ile canlıya giden şey birebir aynı olmalıdır. Bu, her ortam için ayrı imaj derlememen anlamına gelir. Bir kez derlersin, etiketlersin, ortamlar arasında o etiketi taşırsın.

    1. Değişiklik ana dala birleşir ve CI tek bir imaj üretir: siparis-api:a1b2c3d.
    2. İmaj staging'e dağıtılır; otomatik testler ve elle kontrol burada yapılır.
    3. Onaydan sonra aynı etiket canlıya dağıtılır. Yeniden derleme yoktur.
    4. Sorun çıkarsa bir önceki etikete dönülür.
    # Staging'e dağıt
    SURUM=a1b2c3d docker compose -f compose.yml -f compose.staging.yml up -d
    
    # Onaydan sonra AYNI etiketi canlıya al
    SURUM=a1b2c3d docker compose -f compose.yml -f compose.prod.yml up -d
    
    # Sorun çıkarsa bir öncekine dön
    SURUM=9f8e7d6 docker compose -f compose.yml -f compose.prod.yml up -d
    

    latest etiketini bu akışta kullanmamalısın; latest zamanla değişen bir işaretçidir ve staging'de test ettiğinle canlıya gidenin aynı olduğunu garanti etmez. Her dağıtımın izlenebilir bir sürüm etiketi olmalıdır.

    Bu akışı elle yönetmek yerine bildirimsel hâle getirmek istersen, ortam durumunu bir Git deposunda tutup otomatik senkronize eden yaklaşımı GitOps ve Argo CD nedir yazısında anlatıyorum; özellikle birden fazla ortam ve birden fazla kişi varken elle dağıtımın yerini iyi doldurur.

    Sık Yapılan Hatalar ve Tuzaklar#

    Staging'i canlıdan farklı kurmak. Farklı veritabanı sürümü, farklı PHP sürümü, farklı ters proxy ya da eksik bir servis — bunların her biri staging'in doğrulama gücünü sıfırlar. "Staging'de çalışıyordu" cümlesinin arkasında neredeyse her zaman bir yapılandırma farkı vardır. Ortamlar arasındaki farkları yazılı tutmak ve düzenli olarak azaltmak bu sınıfın tek çözümüdür.

    Staging'i arama motorlarına açmak. Staging alan adı indekslenirse, aynı içerik iki adreste görünür ve canlı sitenin sıralamasını olumsuz etkiler. Çözüm hem HTTP başlığı hem robots.txt seviyesinde engellemek, ayrıca temel kimlik doğrulaması koymaktır:

    server {
        server_name staging.firmaniz.com;
    
        # Arama motorlarına kapat
        add_header X-Robots-Tag "noindex, nofollow" always;
    
        # Basit erişim kısıtı
        auth_basic "Staging";
        auth_basic_user_file /etc/nginx/.htpasswd;
    
        location / {
            proxy_pass http://127.0.0.1:3000;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
    

    Canlıda hata ayıklama modunu açık bırakmak. APP_DEBUG=true ile çalışan bir uygulama hata sayfasında yığın izini, dosya yollarını ve bazen ortam değişkenlerini gösterir. Bu, saldırgana sistemin haritasını vermek demektir. Dağıtım kontrol listesinin ilk maddesi bu olmalıdır.

    Ortamlar arasında gizli bilgi paylaşmak. Aynı veritabanı parolası, aynı API anahtarı, aynı JWT imzalama anahtarı — bunlardan biri sızarsa üç ortam birden düşer. Her ortamın kendi değerleri olmalıdır.

    Staging'i uzun süre bakımsız bırakmak. Aylarca güncellenmeyen bir staging, canlıdan o kadar uzaklaşır ki kimse ona güvenmez ve süreç fiilen ölür. Staging'i canlı dağıtımın rutin bir adımı yap; ayrı bir proje gibi davranırsan çürür.

    Tek makinede üç ortamı çalıştırmak. Kaynak paylaşımı nedeniyle staging'deki ağır bir test canlıyı yavaşlatır ve staging'e sızan biri canlının yanı başındadır. Bütçe elverdiğince en azından canlıyı ayrı bir makinede tut; ayrı bir bulut sunucu örneği bu ayrımı ucuza sağlar.

    Sıkça Sorulan Sorular#

    Staging ortamı gerçekten gerekli mi#

    Tek geliştiricili ve düşük riskli bir projede dev ile prod arası doğrudan geçiş kabul edilebilir. Ancak ödeme alan, müşteri verisi tutan ya da birden fazla kişinin katkı verdiği her sistemde staging kısa sürede kendini amorti eder: canlıya çıkan bir hatanın maliyeti, ek bir sunucunun aylık bedelinden neredeyse her zaman yüksektir. Kaynak kısıtlıysa staging'i canlıdan daha küçük ama yapılandırma olarak birebir kurabilirsin.

    Staging ve canlı aynı sunucuda olabilir mi#

    Teknik olarak mümkündür ve küçük projelerde sık yapılır, ancak iki ciddi riski vardır. Birincisi kaynak rekabeti: staging'de çalıştırılan ağır bir göç ya da test, canlının belleğini ve CPU'sunu tüketebilir. İkincisi güvenlik: staging genelde daha gevşek korunur ve oraya sızan biri aynı makinede canlının dosyalarına komşudur. Ayıramıyorsan en azından farklı kullanıcılar, farklı veritabanları ve sıkı kaynak sınırları kullan.

    Canlı veritabanını staging'e kopyalayabilir miyim#

    Doğrudan kopyalamamalısın. Kişisel veriler KVKK kapsamındadır ve staging genellikle daha az korunur; ayrıca ödeme ve kimlik verisi hiçbir şekilde taşınmamalıdır. Doğru yöntem, kopyayı aldıktan sonra isim, e-posta, telefon ve adres gibi alanları maskelemek, hassas tabloları ise tamamen boşaltmaktır. Bu işlemi elle değil, sürüm kontrolünde duran bir betikle yapmak tekrarlanabilirliği sağlar.

    Ortam değişkenlerini nerede saklamalıyım#

    Sunucudaki gerçek değerler, yalnızca uygulama kullanıcısının okuyabildiği bir dosyada (0640 izinli) durmalı ve sürüm kontrolüne girmemelidir. Depoda yalnızca değerleri boş bırakılmış bir .env.example bulunmalıdır. Ekip büyüdükçe bir gizli bilgi yöneticisi kullanmak daha sağlıklıdır, ama küçük kurulumlarda izinleri doğru ayarlanmış bir dosya ve düzenli parola rotasyonu yeterli bir başlangıçtır.

    Hangi ortamda hangi log seviyesi olmalı#

    Geliştirmede debug seviyesi işini kolaylaştırır; her ayrıntıyı görmek istersin. Staging'de info genelde dengelidir: akışı izleyebilirsin ama disk gereksiz yere dolmaz. Canlıda warning ya da error seviyesinde kal ve ayrıntılı hata izlerini kullanıcıya değil yalnızca log dosyasına yaz. Log döndürme (rotation) ayarını her üç ortamda da kurmayı unutma; sınırsız büyüyen log dosyası klasik bir disk dolma sebebidir.

    Kaç ortam kurmalıyım#

    Çoğu proje için üç ortam yeterlidir ve fazlası bakım yükü getirir. Bazı ekipler her özellik dalı için geçici bir önizleme ortamı kurar; bu, konteyner tabanlı bir kurulumda oldukça pratiktir ve staging'i tıkanmaktan kurtarır. Ayrı bir "QA" ya da "UAT" ortamı ise ancak farklı bir grup insan farklı bir doğrulama yapıyorsa anlamlıdır; sırf isim olsun diye kurulan ortamlar kısa sürede bakımsız kalır.

    Kapanış#

    Ortam ayrımı bir sunucu sayısı meselesi değil, bir disiplin meselesidir. Aklında tutman gereken dört alışkanlık şu: staging'i canlının birebir kopyası olarak kur ve öyle tut, ortama bağlı her şeyi koddan çıkarıp yapılandırmaya taşı, staging'e giden veriyi mutlaka anonimleştir ve staging'de test edilen artefaktın aynısını canlıya terfi ettir. Bu dördü, "canlıda neden farklı davrandı" sorusunu büyük ölçüde ortadan kaldırır.

    Ortamları gerçekten ayırmak için birbirinden bağımsız kaynaklara ihtiyacın var. Staging ve canlıyı ayrı makinelerde çalıştırmak için bulut sunucu ve VDS paketlerimize göz atabilir, kurulum ve bakımı üstlenmemizi isterseniz sunucu yönetimi hizmetimizden yararlanabilirsiniz. Canlı ortamın verisini güvence altına almak için yedekleme, mevcut kurulumu taşımak içinse site taşıma hizmetimiz işi devralır.

    Ortam YönetimiStagingDevOps

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.