Açık Kaynak Uygulamalar

    Docker ile WordPress Kurulumu: Compose Dosyasından Canlı Siteye

    Çalışan bir compose dosyasından canlı, SSL'li ve yedeklenebilir bir WordPress kurulumuna kadar uçtan uca kurulum rehberi.

    11 dk okuma Güncellendi: 18 Ağustos 2026

    Elinizde temiz bir Ubuntu sunucusu var, Docker kurulu, alan adının A kaydı da sunucuya bakıyor. Amacınız yarım saat içinde ayakta, HTTPS'li ve gerçekten yedeklenebilir bir WordPress sitesi. İnternette bulduğunuz üç satırlık docker run örnekleri siteyi ayağa kaldırıyor, ama sonra bir soru geliyor: sunucuyu yeniden başlattığınızda veriler duruyor mu, sertifikayı nereye kuracaksınız, 2 MB'lık tema dosyası neden yüklenmiyor?

    Bu rehber tam olarak o boşluğu kapatıyor. Çalışan bir compose.yaml dosyasından başlayıp canlı siteye kadar gideceğiz: WordPress ve MariaDB servislerinin doğru tanımı, verinin adlandırılmış hacimlerde tutulması, sürüm sabitleme, önüne ters proxy koyup Let's Encrypt sertifikası alma, PHP yükleme limitleri, yedeklemenin neden imajla değil hacimle yapıldığı ve konteyner içinden WP-CLI kullanımı.

    Compose sözdizimine hiç aşina değilseniz Docker Compose kullanımı rehberi servis, ağ ve hacim kavramlarının temelini verir; burada doğrudan WordPress'e özgü ayrıntılara odaklanacağız.

    Docker ile WordPress Kurmanın Mantığı ve Sınırları#

    Klasik kurulumda WordPress'i sunucuya kurmak demek, Apache veya Nginx, PHP-FPM, PHP eklentileri ve MySQL'i sisteme kurup birbirine bağlamak demektir. Bir sunucuda iki farklı PHP sürümü isteyen iki site varsa iş hemen karışır.

    Docker bu tabloyu tersine çevirir: WordPress'in resmi imajı, doğru PHP eklentileriyle yapılandırılmış Apache ve PHP'yi birlikte getirir. Siz yalnızca hangi sürümü istediğinizi ve verinin nerede duracağını söylersiniz. Aynı sunucuda PHP 8.2 isteyen eski bir siteyle PHP 8.3 isteyen yeni bir site birbirine dokunmadan çalışabilir.

    Karşılığında iki yeni sorumluluk alırsınız. Birincisi, konteynerin dosya sistemi geçicidir: hacim tanımlamazsanız yüklediğiniz temalar, eklentiler ve medya dosyaları konteyneri yeniden oluşturduğunuz anda kaybolur. İkincisi, sertifika ve alan adı yönetimi artık konteynerin değil önüne koyacağınız ters proxy'nin işidir. Bu iki noktayı baştan doğru kurarsanız gerisi rahat ilerler.

    Dizin Yapısı ve .env Dosyası#

    Her şeyi tek klasörde toplayın; taşımak ve yedeklemek kolaylaşır.

    mkdir -p /opt/wp-ornek && cd /opt/wp-ornek
    touch compose.yaml .env uploads.ini
    chmod 600 .env
    

    Parolaları Compose dosyasının içine gömmeyin. Sürüm kontrolüne girse bile sızmayacak şekilde .env dosyasında tutun:

    DB_NAME=wordpress
    DB_USER=wp_kullanici
    DB_PASSWORD=uzun-ve-rastgele-bir-parola
    DB_ROOT_PASSWORD=bambaska-uzun-bir-parola
    

    .env dosyası aynı dizindeyse Compose onu otomatik okur. Git kullanıyorsanız .gitignore içine eklemeyi unutmayın; sunucuda da chmod 600 ile sadece sahibinin okuyabildiğinden emin olun.

    Çalışan compose.yaml: WordPress ve MariaDB#

    Aşağıdaki dosya doğrudan kullanılabilir. Her satırın neden orada olduğunu altında açıklıyorum.

    services:
      db:
        image: mariadb:11.4
        restart: unless-stopped
        environment:
          MARIADB_DATABASE: ${DB_NAME}
          MARIADB_USER: ${DB_USER}
          MARIADB_PASSWORD: ${DB_PASSWORD}
          MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
        volumes:
          - db_data:/var/lib/mysql
        healthcheck:
          test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
          interval: 10s
          timeout: 5s
          retries: 10
    
      wordpress:
        image: wordpress:6.8-php8.3-apache
        restart: unless-stopped
        depends_on:
          db:
            condition: service_healthy
        environment:
          WORDPRESS_DB_HOST: db:3306
          WORDPRESS_DB_NAME: ${DB_NAME}
          WORDPRESS_DB_USER: ${DB_USER}
          WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
          WORDPRESS_TABLE_PREFIX: wp_
          WORDPRESS_CONFIG_EXTRA: |
            if (isset($$_SERVER['HTTP_X_FORWARDED_PROTO']) && $$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
                $$_SERVER['HTTPS'] = 'on';
            }
        volumes:
          - wp_data:/var/www/html
          - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro
        ports:
          - "127.0.0.1:8080:80"
    
    volumes:
      db_data:
      wp_data:
    

    Dikkat edilecek noktalar:

    • WORDPRESS_DB_HOST: db:3306 — burada IP değil servis adı yazılır. Compose kendi ağını kurar ve servisler birbirini adıyla çözer.
    • depends_on + condition: service_healthy — WordPress, MariaDB'nin sadece başlamasını değil bağlantı kabul edecek hale gelmesini bekler. Bu satır olmadan ilk açılışta "Error establishing a database connection" almanız normaldir.
    • ports: "127.0.0.1:8080:80" — konteyner yalnızca sunucunun kendisine açılır, dış dünyaya değil. Trafiği önündeki ters proxy taşıyacak. Bu yazmadan yalnızca 8080:80 yazarsanız site 8080 portundan sertifikasız olarak internete açılır.
    • WORDPRESS_CONFIG_EXTRA — proxy arkasındaki WordPress'e "bu istek HTTPS ile geldi" demenin yoludur. Compose dosyasında $$ işareti PHP'ye tek $ olarak geçer. Bu blok olmadan yönlendirme döngüsü veya karışık içerik uyarıları çıkar.
    • Adlandırılmış hacimlerwp_data çekirdek dosyaları ve wp-content klasörünü, db_data veritabanını tutar. Hacim mantığının ayrıntısı için Docker hacim yönetimi yazısına bakın.

    Sürüm Sabitleme: latest Etiketi Neden Sorun Çıkarır#

    image: wordpress:latest yazmak kurulum anında en kolay seçenektir ve üretimde en pahalı olanıdır. latest sabit bir sürüm değil hareketli bir işaretçidir: bugün çektiğiniz imaj altı ay sonra bambaşka bir PHP sürümü getirebilir. Sunucuyu yeniden başlattığınızda ya da imajı yenilediğinizde sitenin neden bozulduğunu anlamanız günler alır.

    EtiketNe verirNe zaman kullanılır
    wordpress:latestHer zaman en yeni sürümYerel deneme, tek kullanımlık test
    wordpress:6.8-php8.3-apacheSabit ana sürüm ve PHP sürümüÜretim için önerilen
    wordpress:6.8.2-php8.3-apacheYama sürümüne kadar sabitDeğişikliği tamamen siz kontrol etmek istediğinizde
    mariadb:11.4Uzun destekli (LTS) sürüm dalıVeritabanı için önerilen

    Veritabanı sürümünü sabitlemek daha da kritiktir. MariaDB'nin ana sürümü atladığında veri dizini biçimi değişebilir ve geri dönüş mümkün olmayabilir; bir dizin bir kez yeni sürümle açıldıysa eskiye dönme şansınız kalmaz. Yükseltmeyi ancak yedek aldıktan sonra, bilinçli bir adım olarak yapın. Güncel etiketleri kurulum öncesinde imaj deposundan doğrulamak iyi bir alışkanlıktır.

    İlk Ayağa Kaldırma ve Kurulum Sihirbazı#

    Önce PHP limitlerini taşıyacak uploads.ini dosyasını yazın:

    file_uploads = On
    upload_max_filesize = 64M
    post_max_size = 64M
    memory_limit = 256M
    max_execution_time = 300
    

    Ardından yığını başlatın:

    docker compose up -d
    docker compose ps
    docker compose logs -f wordpress
    

    docker compose ps çıktısında db servisinin healthy, wordpress servisinin running olduğunu görmelisiniz. Bu aşamada site http://127.0.0.1:8080 üzerinden yalnızca sunucudan erişilebilir durumda. Test etmek için sunucuda:

    curl -I http://127.0.0.1:8080
    

    HTTP/1.1 302 Found ve Location: .../wp-admin/install.php cevabı gelmesi WordPress'in ayakta olduğunu gösterir. Kurulum sihirbazını tarayıcıdan tamamlamak için önce ters proxy'yi kuralım — sihirbazı HTTP üzerinden tamamlayıp sonra HTTPS'e geçmek, veritabanına http:// ile başlayan adres yazdığı için ek düzeltme işi çıkarır.

    Önüne Ters Proxy Koyup SSL Almak#

    Konteyner sertifika işiyle uğraşmaz; sunucudaki Nginx dış dünyadan 80/443 portlarını dinler ve trafiği 8080'e aktarır. Bu ayrım, aynı sunucuda ileride ikinci bir site açmak istediğinizde de işinizi kolaylaştırır.

    server {
        listen 80;
        server_name ornek.com www.ornek.com;
    
        client_max_body_size 64m;
    
        location / {
            proxy_pass http://127.0.0.1:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
    

    X-Forwarded-Proto başlığı, Compose dosyasındaki WORDPRESS_CONFIG_EXTRA bloğunun okuduğu değerdir; ikisi birlikte çalışır. client_max_body_size değerini de PHP tarafındaki upload_max_filesize ile aynı tutun, yoksa büyük dosyalarda 413 hatası alırsınız.

    Yapılandırmayı sınayıp yükledikten sonra sertifikayı alın:

    sudo nginx -t && sudo systemctl reload nginx
    sudo certbot --nginx -d ornek.com -d www.ornek.com
    

    Certbot 443 bloğunu ve HTTP'den HTTPS'e yönlendirmeyi kendisi ekler. Ters proxy başlıklarının ayrıntısı için Nginx ters proxy yapılandırması, sertifika süreci ve otomatik yenileme için Let's Encrypt ücretsiz SSL yazılarına bakabilirsiniz.

    Artık https://ornek.com adresinden kurulum sihirbazını tamamlayabilirsiniz. Sihirbaz site adresini otomatik olarak https:// ile kaydeder.

    Bu düzenin görünmeyen bir avantajı daha var: aynı sunucuya ikinci bir WordPress kurmak istediğinizde yeni bir klasör açıp Compose dosyasını kopyalamanız, port eşlemesini 127.0.0.1:8081:80 yapmanız ve Nginx'e ikinci bir server bloğu eklemeniz yeterlidir. İki site birbirinin PHP sürümünü, eklentisini veya veritabanı kullanıcısını hiçbir şekilde etkilemez. Klasik kurulumda aynı ayrımı sağlamak çok daha zahmetlidir.

    Upload Boyutu ve PHP Limitlerini Ayarlamak#

    "Yüklediğiniz dosya php.ini içindeki upload_max_filesize değerini aşıyor" hatası Docker kurulumlarında sık görülür, çünkü resmi imajın varsayılanı çoğu tema paketinden düşüktür. Sınır üç ayrı katmanda kontrol edilir ve en düşük olanı kazanır:

    KatmanAyarNerede
    Ters proxyclient_max_body_sizeSunucudaki Nginx yapılandırması
    PHPupload_max_filesize, post_max_sizeuploads.ini (konteynere bağlanan dosya)
    PHPmemory_limit, max_execution_timeAynı dosya

    uploads.ini dosyasını değiştirdikten sonra ayarın uygulanması için konteynerin yeniden başlatılması gerekir:

    docker compose restart wordpress
    docker compose exec wordpress php -i | grep -E 'upload_max_filesize|post_max_size|memory_limit'
    

    Değerler beklediğiniz gibi görünmüyorsa dosya büyük ihtimalle konteynere bağlanmamıştır; docker compose exec wordpress ls -l /usr/local/etc/php/conf.d/ ile kontrol edin. Bu limitlerin anlamları ve hangi durumda hangisinin devreye girdiği konusunda PHP upload_max_filesize ayarı yazısı ayrıntılı bir referanstır.

    Yedekleme: İmajı Değil Hacmi ve Veritabanını Yedekleyin#

    Docker'a yeni geçenlerin en sık yaptığı hata docker commit ile imaj yedeği almak ve bunu site yedeği sanmaktır. İmaj yalnızca uygulamayı içerir; içeriğiniz hacimlerde durur. Gerçek yedek iki parçadan oluşur: veritabanı dökümü ve wp_data hacmi.

    Önce hacimlerin gerçek adlarını görün — Compose bunlara proje dizininin adını ön ek olarak ekler:

    docker volume ls
    

    Veritabanı dökümü:

    docker compose exec -T db \
      mariadb-dump -u root -p"$DB_ROOT_PASSWORD" --single-transaction wordpress \
      > db-$(date +%F).sql
    

    Dosya hacmi (geçici bir yardımcı konteyner üzerinden):

    docker run --rm \
      -v wpornek_wp_data:/data:ro \
      -v "$PWD":/backup \
      alpine tar czf /backup/wp_data-$(date +%F).tar.gz -C /data .
    

    Geri dönüş de aynı mantıkla, ters sırayla işler:

    docker compose up -d db
    docker compose exec -T db mariadb -u root -p"$DB_ROOT_PASSWORD" wordpress < db-2026-08-18.sql
    docker run --rm -v wpornek_wp_data:/data -v "$PWD":/backup \
      alpine sh -c "tar xzf /backup/wp_data-2026-08-18.tar.gz -C /data"
    docker compose up -d
    

    Bu iki dosya elinizdeyse siteyi bambaşka bir sunucuda ayağa kaldırabilirsiniz. Yedekleri sunucunun kendisinde bırakmayın; sunucu kaybı yedeği de götürür. Ayda bir kez geri yükleme denemesi yapın — hiç denenmemiş bir yedek, ihtiyaç anına kadar yedek olduğu doğrulanmamış bir dosyadır.

    Konteyner İçinden WP-CLI Kullanmak#

    WordPress'in resmi imajında WP-CLI bulunmaz; ayrı bir servis olarak eklenir. profiles anahtarı sayesinde bu servis normal up komutunda çalışmaz, yalnızca siz çağırınca ayağa kalkar:

      wpcli:
        image: wordpress:cli
        user: "33:33"
        depends_on:
          - db
        volumes:
          - wp_data:/var/www/html
        environment:
          WORDPRESS_DB_HOST: db:3306
          WORDPRESS_DB_NAME: ${DB_NAME}
          WORDPRESS_DB_USER: ${DB_USER}
          WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
        profiles: ["cli"]
    

    user: "33:33" satırı önemlidir. WP-CLI imajı Alpine tabanlıdır ve içindeki www-data kullanıcısının numarası, Debian tabanlı WordPress imajındakiyle aynı değildir. Bu satır olmadan WP-CLI'nin oluşturduğu dosyalar yanlış sahiplikle yazılır ve WordPress sonradan onları düzenleyemez.

    Kullanımı:

    docker compose run --rm wpcli wp core version
    docker compose run --rm wpcli wp plugin list
    docker compose run --rm wpcli wp search-replace 'http://ornek.com' 'https://ornek.com' --skip-columns=guid
    docker compose run --rm wpcli wp cache flush
    

    Alan adı değiştirme, toplu eklenti güncelleme ve kullanıcı parolası sıfırlama gibi işleri panelden değil buradan yapmak hem hızlıdır hem de panel açılmadığında tek çıkış yoludur. Komut listesi için WP-CLI kullanımı yazısına bakabilirsiniz.

    Güncelleme ve Sık Karşılaşılan Hatalar#

    İmaj güncellemesi iki komutluk bir iştir; verileriniz hacimde olduğu için konteynerlerin yeniden yaratılması içeriğe dokunmaz:

    docker compose pull
    docker compose up -d
    docker compose run --rm wpcli wp core version
    

    Son satırı atlamayın: WordPress çekirdek dosyaları hacimde yaşadığı için imaj etiketini yükseltmeniz çekirdek sürümünüzü otomatik değiştirmeyebilir. Gerçekte hangi sürümde olduğunuzu doğrulayın ve çekirdek güncellemesi için tek bir yol seçip ona sadık kalın — ya imaj etiketi ya WordPress paneli, ikisi birden değil.

    Sahada en sık karşılaşılan arızalar:

    BelirtiSebepÇözüm
    "Error establishing a database connection"Veritabanı henüz hazır değil ya da host adı yanlışdepends_on + condition: service_healthy, WORDPRESS_DB_HOST: db:3306
    .env parolasını değiştirdim, bağlanmıyorParola değişkenleri yalnızca hacim boşken, ilk kurulumda uygulanırVeritabanına girip ALTER USER ile parolayı elle değiştirin
    Sonsuz yönlendirme döngüsüProxy arkasında HTTPS algılanmıyorWORDPRESS_CONFIG_EXTRA bloğu ve X-Forwarded-Proto başlığı
    Karışık içerik (mixed content) uyarısısiteurl/home hâlâ http://wp search-replace ile adresleri düzeltin
    413 Request Entity Too LargeNginx gövde limiti düşükclient_max_body_size değerini yükseltin
    Eklenti kurarken FTP bilgisi isteniyorwp-content sahipliği yanlışdocker compose exec wordpress chown -R www-data:www-data /var/www/html/wp-content
    address already in use8080 portu başka bir servis tarafından kullanılıyorCompose'daki port eşlemesini değiştirin

    İkinci satırdaki tuzağa özellikle dikkat edin: MariaDB imajı ortam değişkenlerindeki kullanıcı ve parolayı yalnızca veri dizini boşken, yani ilk başlatmada oluşturur. Sonradan .env içindeki parolayı değiştirdiğinizde veritabanındaki kullanıcı eski parolayla kalır ve WordPress bağlanamaz.

    Son olarak, tek bir bayrağın farkını aklınızda tutun. docker compose down konteynerleri ve ağı kaldırır, hacimlere dokunmaz — güvenlidir. docker compose down -v ise adlandırılmış hacimleri de siler: veritabanınız, medya kütüphaneniz ve eklentileriniz geri dönüşsüz gider. İnternette bulduğunuz "şunu dene" tarzı komutları yapıştırmadan önce sonunda -v olup olmadığına bakın; bu bayrak yüzünden kaybedilen site sayısı sanıldığından fazladır.

    Sıkça Sorulan Sorular#

    Docker ile kurulan WordPress'te veriler nerede tutulur?#

    İki ayrı adlandırılmış hacimde. wp_data hacmi /var/www/html dizinini, yani WordPress çekirdek dosyalarını, temaları, eklentileri ve medya yüklemelerini tutar. db_data hacmi ise MariaDB'nin veri dizinini barındırır. Bu hacimler konteynerlerden bağımsız yaşar; konteyneri silseniz bile veriler durur. Hacim tanımlamayı unutursanız her yeniden oluşturmada içeriğiniz sıfırlanır.

    wordpress:latest yerine neden sabit sürüm kullanmalıyım?#

    latest etiketi sabit bir sürüme değil o anki en yeni sürüme işaret eder. Aylar sonra imajı yenilediğinizde farkında olmadan yeni bir PHP sürümüne geçebilir, eski bir eklenti bu geçişte kırılabilir ve sorunun kaynağını bulmak zorlaşır. wordpress:6.8-php8.3-apache gibi bir etiket, yükseltmeyi bilinçli bir karar haline getirir ve yedek almadan sürüm atlamanızı engeller.

    Docker'daki WordPress'e nasıl SSL sertifikası kurarım?#

    Sertifikayı konteynerin içine kurmazsınız. Konteyneri yalnızca 127.0.0.1:8080 adresine açar, sunucuda çalışan Nginx'i ters proxy olarak yapılandırır ve sertifikayı certbot --nginx ile bu Nginx üzerine alırsınız. Konteynere HTTPS bilgisini X-Forwarded-Proto başlığı taşır; WordPress'in bunu okuması için WORDPRESS_CONFIG_EXTRA içindeki blok gereklidir, yoksa yönlendirme döngüsü oluşur.

    Konteyner içindeki WordPress'e nasıl WP-CLI kurarım?#

    Resmi WordPress imajında WP-CLI yoktur; wordpress:cli imajını ayrı bir servis olarak ekleyip aynı wp_data hacmine bağlarsınız. profiles: ["cli"] tanımı sayesinde bu servis sürekli çalışmaz, yalnızca docker compose run --rm wpcli wp ... dediğinizde ayağa kalkar. user: "33:33" satırını eklemeyi unutmayın, aksi halde oluşturulan dosyaların sahipliği WordPress konteyneriyle uyuşmaz.

    Yedek almak için imajı kaydetmem yeterli mi?#

    Hayır. İmaj yalnızca uygulama dosyalarını içerir, sizin içeriğinizi değil. Gerçek yedek iki parçadır: mariadb-dump ile alınan veritabanı dökümü ve wp_data hacminin arşivi. Hacim arşivini geçici bir yardımcı konteyner üzerinden tar ile alabilirsiniz. Bu iki dosyayla siteyi başka bir sunucuda aynı Compose dosyasıyla ayağa kaldırabilirsiniz; yedekleri sunucunun dışında saklayın.

    .env dosyasındaki veritabanı parolasını değiştirdim ama site bağlanmıyor, neden?#

    MariaDB imajı ortam değişkenlerindeki kullanıcı ve parolayı yalnızca veri dizini boşken, yani ilk başlatmada oluşturur. Hacim zaten doluysa yeni parola görmezden gelinir ve kullanıcı eski parolayla kalır. Çözüm, veritabanı konteynerine girip ALTER USER komutuyla parolayı elle güncellemektir. Sıfırdan kurulum yapıyorsanız db_data hacmini silip yeniden başlatmak da işe yarar, ancak bu mevcut verilerinizi tamamen siler.

    DockerWordPressKurulum

    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.