2026'da Symfony ve Docker: Geliştirme Ortamı ve Üretim Dağıtımı
2026 için eksiksiz bir Symfony Docker iş akışı: FrankenPHP geliştirme ortamı, çok aşamalı üretim imajı, worker modu, şifreli sırlar, imaj optimizasyonu ve migration'lı kesintisiz dağıtımlar.

Bu Symfony Docker rehberi eksiksiz bir konteyner iş akışı kurar: tekrarlanabilir bir yerel geliştirme ortamı ve sağlamlaştırılmış bir üretim imajı; her ikisi de FrankenPHP ve Symfony 7.4 LTS üzerine oturur. Symfony'yi konteynerlerde çalıştırmak, dizüstü bilgisayarlar ile sunucular arasındaki klasik ortam kayması sorununu ortadan kaldırır ve her dağıtımı, kırılgan bir dizi manuel sunucu komutu yerine tek ve değişmez bir yapıtın gönderilmesine dönüştürür.
Resmi Symfony Docker şablonu artık çalışma zamanı olarak FrankenPHP ile geliyor ve geleneksel Nginx artı PHP-FPM ikilisinin yerini alıyor. Tek bir ikili dosya HTTP/2 ve HTTP/3'ü sunar ve çekirdeği her çağrıda yeniden kurmak yerine istekler arasında açık tutarak Symfony'yi kalıcı worker modunda çalıştırır.
Symfony için Docker Compose Geliştirme Ortamı
İyi bir geliştirme kurulumu, her katkıda bulunana aynı PHP sürümünü, aynı eklentileri ve aynı veritabanını host makineye dokunmadan sağlar. Docker Compose bu yığını bildirimsel biçimde tanımlar. Aşağıdaki örnek, yerel bir Dockerfile üzerinden derlenen bir FrankenPHP konteynerini bir PostgreSQL 17 servisiyle eşleştirir ve ikisini varsayılan Compose ağı üzerinde birbirine bağlar.
# compose.yaml
services:
php:
build:
context: .
target: frankenphp_dev
ports:
- "80:80"
- "443:443"
volumes:
- ./:/app # bind mount: edits on the host appear instantly
- caddy_data:/data # persist TLS certificates across restarts
environment:
DATABASE_URL: "postgresql://app:app@database:5432/app?serverVersion=17"
depends_on:
- database
database:
image: postgres:17-alpine
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: app
volumes:
- db_data:/var/lib/postgresql/data
volumes:
caddy_data:
db_data:php servisindeki bind mount, proje kök dizinini konteynerin içine eşler; böylece dosya değişiklikleri imajı yeniden derlemeden etkinleşir. FrankenPHP ilk açılışta yerel bir TLS sertifikası üretir; 443 portunun açılmasının ve caddy_data'nın adlandırılmış bir volume olmasının nedeni budur: sertifika docker compose down sonrasında da varlığını korur. DSN içindeki serverVersion=17 ipucu, Doctrine'in her bağlantıda bir sürüm tespit sorgusu çalıştırmasını atlamasını sağlar.
Yığın çalışmaya başladığında her Symfony komutu host üzerinde değil php konteyneri içinde yürütülür; bu da doğru PHP sürümünü ve eklentilerini garanti eder. Veritabanı oluşturmak, migration'ları çalıştırmak veya bir kabuk açmak, docker compose exec üzerinden aynı deseni izler.
Symfony ve Composer çağrılarını, konteynerin PHP çalışma zamanına karşı çalıştırmak için docker compose exec php ile öne ekleyin; örneğin docker compose exec php bin/console make:entity veya docker compose exec php composer require symfony/uid. Host'ta hiçbir zaman PHP kurulu olması gerekmez.
Symfony Üretimi için Çok Aşamalı Dockerfile
Bir çok aşamalı Dockerfile ortak bir temeli paylaşır ve ardından bir geliştirme hedefi ile bir üretim hedefine ayrılır. Üretim aşaması yalnızca çalışma zamanı bağımlılıklarını kurar, Composer'ın geliştirme paketlerini dışarıda bırakır ve önbelleği derleme sırasında ısıtarak çalışan konteynerin anında başlamasını sağlar.
# Dockerfile
FROM dunglas/frankenphp:1-php8.4 AS base
WORKDIR /app
# Install the PHP extensions Symfony relies on
RUN install-php-extensions \
intl \
opcache \
pdo_pgsql \
zip
COPY /composer /usr/bin/composer
# --- Development target ---
FROM base AS frankenphp_dev
ENV APP_ENV=dev
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
# --- Production target ---
FROM base AS frankenphp_prod
ENV APP_ENV=prod
ENV FRANKENPHP_CONFIG="worker ./public/index.php"
ENV APP_RUNTIME="Runtime\FrankenPhpSymfony\Runtime"
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# Layer caching: copy manifests first, install, then copy the source
COPY composer.json composer.lock symfony.lock ./
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist
COPY . .
RUN composer dump-autoload --no-dev --classmap-authoritative \
&& composer dump-env prod \
&& bin/console cache:warmupcomposer.json ile kilit dosyalarını kaynağın geri kalanından önce kopyalamak temel optimizasyondur: Docker, bağımlılık kurulum katmanını önbelleğe alır ve bunu yalnızca manifest dosyaları değiştiğinde yeniden çalıştırır, her kod düzenlemesinde değil. --classmap-authoritative bayrağı Composer'a classmap'in eksiksiz olduğunu bildirir; böylece otomatik yükleyici çalışma zamanında dosya sistemi aramalarına asla geri düşmez. Önbelleği derleme sırasında ısıtmak, ilk üretim isteğinin tamamen derlenmiş bir konteynere ulaşması anlamına gelir.
FrankenPHP Worker Modu ve Symfony Runtime
Geleneksel PHP-FPM, Symfony çekirdeğini kurar, tek bir isteği işler ve her şeyi atar. Worker modu ise çekirdeği ve bağımlılık enjeksiyon konteynerini istekler arasında canlı tutar; gecikmedeki tasarrufların büyük bölümü buradan gelir. runtime/frankenphp-symfony paketi bunu Symfony Runtime bileşeni ve standart public/index.php başlangıç dosyası üzerinden mümkün kılar.
use App\Kernel;
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
return function (array $context) {
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};Dockerfile'da tanımlanan FRANKENPHP_CONFIG="worker ./public/index.php" satırı bu döngüyü etkinleştirir. Konteyner yeniden kullanıldığı için isteğe özgü durum tutan her servisin kendini istekler arasında sıfırlaması gerekir. Symfony framework servislerini otomatik olarak yönetir, ancak durum tutan özel servislerin Symfony\Contracts\Service\ResetInterface arayüzünü uygulaması ve kernel.reset ile etiketlenmesi gerekir; böylece biriken durum bir sonraki isteğe sızmaz. Bu, uzun süre çalışan Messenger worker'larının gerektirdiği disiplinin aynısıdır ve karşılığında aynı donanımda PHP-FPM'e göre kat kat daha yüksek istek verimi elde edilir.
Yerel geliştirmede FrankenPHP'yi --watch bayrağıyla çalıştırmak, dosyalar değiştiğinde worker'ı otomatik olarak yeniden başlatır; böylece worker modu düzenle-yenile döngüsünün önüne geçmez.
Symfony'de FrankenPHP ile PHP-FPM Karşılaştırması
FrankenPHP ile klasik Nginx artı PHP-FPM yığını arasındaki tercih, hem Dockerfile'ı hem de çalışma zamanı davranışını biçimlendirir. Aşağıdaki tablo, bir Symfony üretim kurulumu açısından pratik farkları özetler.
| Boyut | FrankenPHP worker modu | Nginx + PHP-FPM | |--------|------------------------|-----------------| | Konteynerler | Bir | İki (web sunucusu + PHP) | | Çekirdek başlatma | Worker başına bir kez | İstek başına bir kez | | HTTP/3 | Yerleşik | Ek yapılandırma gerektirir | | TLS | Otomatik (Caddy) | Manuel sertifika kurulumu | | Durum yönetimi | İstekler arası sıfırlama şart | İstek başına yalıtılmış | | Soğuk başlatma maliyeti | Bir kez, açılışta ödenir | Her istekte ödenir |
Worker modu, pahalı çekirdek ve konteyner derleme adımını binlerce isteğe yaydığı için verimde öne geçer. Global değişkenlere veya her işlem için taze bir süreç varsayan üçüncü taraf kütüphanelere dayanan uygulamalarda PHP-FPM hâlâ geçerliliğini korur; çünkü bunlar yeniden kullanılan bir worker altında bozulur.
İstek verisini özel bir özellikte önbelleğe alan bir servis, kendini sıfırlamadıkça bir isteğin verisini bir sonrakine sunar. Üretimde worker moduna geçmeden önce singleton'ları, olay abonelerini ve güvenlik belirteci ya da mevcut isteği tutan her şeyi denetleyin.
Symfony mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Üretimde Ortam Değişkenleri ve Sırların Yönetimi
Symfony yapılandırmayı ortam değişkenlerinden okur ve üretimde bunları sağlamanın iki sağlam yolu vardır. İlki, orkestratör veya Compose dosyası aracılığıyla düz değişkenler enjekte etmektir. İkincisi, hassas değerleri depoda şifreli metin olarak saklayan ve çalışma zamanında tek bir özel anahtarla çözen Symfony'nin şifreli sır kasasıdır.
# Generate the keypair; commit the public key, keep the private key out of the image
php bin/console secrets:generate-keys
# Encrypt a value into config/secrets/prod/
php bin/console secrets:set DATABASE_URL
# In production, expose only the decryption key to the container
export SYMFONY_DECRYPTION_SECRET="$(cat config/secrets/prod/prod.decrypt.private.php)"Dockerfile'daki composer dump-env prod adımı .env dosyalarını tek ve optimize edilmiş bir .env.local.php dosyasında derler; bu da dotenv dosyalarını açılışta ayrıştırmanın çalışma zamanı maliyetini ortadan kaldırır. Gerçek konteyner ortamında tanımlanan herhangi bir değişken yine de derlenmiş varsayılanları geçersiz kılar; dolayısıyla orkestratör tarafından sağlanan sırlar her zaman üstün gelir. Symfony dağıtım kılavuzu tam öncelik sırasını belgeler, ancak pratik kural basittir: APP_SECRET veya veritabanı kimlik bilgilerini asla imaja gömmeyin, bunları konteyner başlangıcında enjekte edin.
Symfony Üretim İmajını Optimize Etmek
İki OPcache ayarı, üretimdeki performans farkının büyük bölümünü açıklar: zaman damgası doğrulamasını kapatmak ve ön yüklemeyi (preloading) etkinleştirmek. Bir konteyner imajı değişmez olduğundan kaynak çalışma zamanında hiç değişmez; bu nedenle OPcache düzenleme olup olmadığını kontrol etmek için dosyaları asla stat etmemelidir. Ön yükleme bir adım daha ileri gider ve Symfony ile uygulama sınıflarını sunucu başlangıcında bir kez paylaşımlı belleğe yükler.
; docker/php/prod.ini
opcache.enable=1
opcache.preload=/app/var/cache/prod/App_KernelProdContainer.preload.php
opcache.preload_user=root
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0 ; immutable image: never re-check the filesystem
realpath_cache_size=4096K
realpath_cache_ttl=600Symfony yukarıda listelenen preload.php dosyasını cache:warmup sırasında üretir; dolayısıyla dosya, derlemeden sonra imajda zaten mevcuttur. OPcache ön yükleme bu sınıfları başlangıçta bağlar ve bunlara ihtiyaç duyan ilk istekte derleme adımını atlar. validate_timestamps=0 ayarı yalnızca yeni bir sürümün yeni bir imaj anlamına geldiği değişmez dağıtımlarda güvenlidir; değiştirilebilir bir host'ta eski kodu sunardı. max_accelerated_files değerini gerçek dosya sayısının üzerinde tutmak, tüm framework'ün tahliye olmadan önbellekte kalmasını sağlar.
.dockerignore ile Symfony İmaj Boyutunu Küçültmek
Eksik bir .dockerignore dosyası derlemeyi sessizce şişirir. Onsuz, COPY . . adımı yerel vendor dizinini, geliştirmedeki var/cache'i, node derleme çıktısını ve .git geçmişini doğrudan imaja ve derleme bağlamına gönderir. Bunları hariç tutmak imajı küçültür, derleme bağlamının daemon'a yüklenmesini hızlandırır ve geliştirme yapıtlarının yeni kurulmuş üretim bağımlılıklarının üzerine yazmasını engeller.
# .dockerignore
/.git/
/vendor/
/node_modules/
/var/
/.env.local
/.env.*.local
/tests/
/docker/
compose*.yaml
Dockerfile/vendor/'ı yok saymak en önemlisidir: üretim aşaması composer install --no-dev çalıştırır, dolayısıyla host'un geliştirme kokan vendor dizinini göndermek bu adımı tamamen boşa çıkarır. /var/'ı hariç tutmak geliştirme önbelleğini ve günlüklerini imajın dışında tutar; böylece derleme zamanındaki cache:warmup temiz bir üretim önbelleği üretir. Çok aşamalı derleme ve Alpine tabanlı eklentilerle birleştiğinde, yalın bir Symfony imajı genellikle 150 MB'nin oldukça altına iner.
Symfony Konteynerlerini Dağıtmak ve Migration Çalıştırmak
Üretim Compose dosyası, yerel olarak derlemek yerine bir kayıt defterinden (registry) önceden derlenmiş bir imaja başvurur ve orkestratörün yalnızca yanıt veren bir konteynere trafik yönlendirmesi için bir sağlık kontrolü tanımlar. Sırlar imaj katmanları olarak değil, ortam değişkenleri olarak ulaşır.
# compose.prod.yaml
services:
php:
image: registry.example.com/app:${TAG}
environment:
APP_ENV: prod
APP_SECRET: ${APP_SECRET}
DATABASE_URL: ${DATABASE_URL}
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost/health"]
interval: 10s
timeout: 3s
retries: 3
restart: unless-stoppedSağlık kontrolü bir /health rotasını curl'ler, dolayısıyla uygulamanın böyle bir rota sunması gerekir. Veritabanına dokunmadan 200 döndüren minimal bir controller, kontrolü hızlı tutar ve geçici bir veritabanı kesintisi sırasında konteynerin sağlıksız işaretlenmesini önler. Veritabanı hazırlığı önemli olduğunda, hafif bir sorgu çalıştıran ikinci ve daha derin bir prob eklenebilir, ancak canlılık uç noktası basit kalır.
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;
final class HealthController extends AbstractController
{
#[Route('/health', name: 'health', methods: ['GET'])]
public function __invoke(): JsonResponse
{
return new JsonResponse(['status' => 'ok']);
}
}Veritabanı migration'ları, uygulama açılışının içinde değil, yeni konteynerler trafik almadan önce ayrı bir adım olarak çalıştırılmalıdır. Bunları tek seferlik bir konteynerde çalıştırmak, birden fazla uygulama kopyası paralel olarak başlasa bile şemanın her sürümde tam olarak bir kez güncellenmesini garanti eder.
# deploy.sh
set -euo pipefail
docker compose -f compose.prod.yaml pull
# Apply migrations once, in an isolated container
docker compose -f compose.prod.yaml run --rm php \
bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration
# Start replicas and wait for the health check to pass
docker compose -f compose.prod.yaml up -d --wait--wait bayrağı her servis sağlıklı olduğunu bildirene kadar bloklar; böylece dağıtım betiğine bırak-ve-unut bir başlangıç yerine gerçek bir başarı sinyali verir. Bunu Kubernetes veya Docker Swarm gibi bir orkestratördeki kademeli güncellemeyle eşleştirmek, kesintisiz sürümler sağlar: eski konteynerler, yenileri sağlık kontrolünü geçene kadar hizmet vermeye devam eder. Bu iş akışının hedeflediği framework sürümlerine daha geniş bir bakış için Symfony platform genel bakışı ve Symfony 8 ve PHP 8.4 özellik kılavuzu güncel sürüm hattında nelerin değiştiğini ele alır.
Sonuç
- Geliştirme ve üretim imajlarının tek bir temeli paylaşması, üretim hedefinin ise Composer geliştirme bağımlılıkları olmadan gönderilmesi için çok aşamalı bir Dockerfile kullanın
- Bağımlılık kurulum katmanını kod değişiklikleri boyunca önbellekte tutmak için
composer.jsonve kilit dosyalarını kaynaktan önce kopyalayın - Kurulan çekirdeği istekler arasında yeniden kullanmak için FrankenPHP'yi worker modunda çalıştırın ve durum sızıntılarını önlemek için durum tutan servisleri
ResetInterfaceile sıfırlayın - Çalışan konteynerin tamamen başlatılmış olarak başlaması için Symfony önbelleğini ısıtın ve
.env'i derleme zamanındadump-env prodile derleyin - Üretimde OPcache ön yüklemeyi etkinleştirin ve
validate_timestamps=0ayarlayın; bu, tam olarak imaj değişmez olduğu için güvenlidir APP_SECRET'i, veritabanı kimlik bilgilerini ve kasa çözme anahtarını imaj katmanlarından uzak tutun; bunları konteyner başlangıcında ortam değişkenleri olarak enjekte edin- Doctrine migration'larını, yeni kopyalar trafik almadan önce tek seferlik bir konteynerde uygulayın ve dağıtımı
up -d --waitile sağlık kontrolüne bağlayın
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Etiketler
Paylaş
İlgili makaleler

Symfony 8: 2026'daki Yeni Ozellikler, PHP 8.4 Lazy Objects ve Mulakat Sorulari
Symfony 8'in yeni ozelliklerini, PHP 8.4 lazy objects entegrasyonunu, cok adimli formlari ve 2026 mulakat sorularini kod ornekleriyle kesfet.

Symfony Live Components ve UX 3.0: 2026'da JavaScript Olmadan Reaktif Uygulamalar
Symfony Live Components ve UX 3.0 ile JavaScript yazmadan reaktif arayuzler olusturma rehberi. PHP ve Twig ile dinamik bilesenler icin kapsamli tutorial ve pratik ornekler.

Doctrine ORM: Symfony'de İlişkilerde Ustalaşmak
Symfony'de Doctrine ORM ilişkileri için kapsamlı rehber. OneToMany, ManyToMany, yükleme stratejileri ve pratik örneklerle performans optimizasyonu.