İçindekiler
- 1 Hikaye Gibi Bir Giriş: Küçük Bir Tıkanıklık, Büyük Bir Akış
- 2 Resmi Netleştirelim: WordPress, Nginx, MariaDB ve Redis Nasıl Konuşur?
- 3 Kalıcı Hacimler: Dosyalar Nerede, Neden Bu Kadar Önemli?
- 4 docker-compose.yml: Çalışır Bir Örnek ve Küçük Sırlar
- 5 Nginx ve Redis: Vitrini Parlatmak, İçeriği Hızlandırmak
- 6 Otomatik Yedek ve Geri Yükleme ile Güncelleme Akışı: Önce Emniyet, Sonra Cesaret
- 7 Kapanış: Bir Kez Kur, Hep Güven
Hikaye Gibi Bir Giriş: Küçük Bir Tıkanıklık, Büyük Bir Akış
Hiç canlı sitede minik bir düzenlemeyi yayınlayıp, sonra “Bir şeyler yavaşladı ama nereden?” diye ekran karşısında iç çektiniz mi? Ben bir sabah tam da bu tabloyla karşılaştım. Editör bir görseli değiştirmiş, eklentilerden biri de hem veritabanını hem disk yazmalarını biraz sıkıştırmış. Sayfalar açılıyor ama bir ağırlık var. O an düşündüm: Şu kurduğumuz WordPress’i biraz daha sağlam bir altyapının üstüne koymanın zamanı gelmedi mi?
İşte bu yazı tam oradan doğdu. Docker Compose ile WordPress + Nginx + MariaDB + Redis kurulumunu tek bir nefeste anlatmak istiyorum. Üstelik sadece çalıştırıp bırakmak değil; kalıcı hacimler ile verinin güvenini, otomatik yedek akışıyla iç huzuru ve güncelleme akışı ile “hadi bakalım, korkmadan bastım güncelle” özgüvenini beraber kuracağız. Mesela şöyle düşünün: Bir kez düzenini kurdunuz mu, sonrası rutin bir sabah kahvesi gibi. Kokusu tanıdık, tadı dengeli.
Yol boyunca basit örnekler, küçük notlar ve “bunu böyle yapınca rahat ediyorsun” diyeceğim anlar olacak. Teknik terimleri zorlamadan, ama işin püf noktalarını saklamadan anlatacağım. Hazırsanız başlayalım.
Resmi Netleştirelim: WordPress, Nginx, MariaDB ve Redis Nasıl Konuşur?
Gözünüzde şöyle canlandırın: Sahnenin ortasında WordPress var. Kulağında bir yanda MariaDB’nin verileri, diğer yanda Redis’in hızlı fısıltıları. Ön tarafta ise sahnenin ışıklarını ayarlayan Nginx. Ziyaretçi geldi mi Nginx karşılar, WordPress’e doğru iletir, WordPress de “veriyi soralım” der ve MariaDB’ye döner. Aynı sorular tekrar tekrar sorulmasın diye Redis devreye girer, “Bunun cevabı bende var” der ve site birden ferahlar.
Docker Compose burada bütün bu oyuncuları tek bir yerde toplar. “Şu konteyner Nginx, şu WordPress, şu MariaDB, şu da Redis” diye dosyada anlatırsınız, hepsini aynı ağda buluşturursunuz. En güzel tarafı, her biri kendi kutusunda. Yani Nginx’i güncellersiniz, MariaDB’ye dokunmaz. Redis’i ayarlarsınız, WordPress huzurla işine devam eder. Küçük bir tiyatro ekibi gibi, herkes rolünü bilir.
Bu kompozisyonun gücü, parçaların net ayrılmasından geliyor. Siz sadece akışı yönetirsiniz. Bir dosyada (Compose) “şu portlar, şu hacimler, şu değişkenler” dersiniz ve tekrar edilebilir bir düzen kurarsınız. Tam da bu yüzden harekete geçiyoruz.
Kalıcı Hacimler: Dosyalar Nerede, Neden Bu Kadar Önemli?
WordPress’te verinin kalbi iki yerde atar: veritabanı ve wp-content. Biri yazılarınız, ayarlarınız, yorumlarınız; diğeri temalar, eklentiler, görseller. Konteynerler gelip geçicidir; bugün var, yarın güncellenir. Ama bu iki set, yani db_data ve wp_data, kalıcı olmalı. İşte bu yüzden volume kullanırız.
Redis için de kalıcılık isteyebilirsiniz. Bazı kurulumlarda Redis sadece geçici bellektir; kapandığında silinse de sorun olmaz. Ama bazen “AOF” denilen günlük tutma ayarıyla kalıcılaştırırsınız. Bu, özellikle nesne önbelleğini uzun süreli saklamak istediğinizde faydalı olur. Ben genelde “işimi görsün, ama kapansın da dünyanın sonu olmasın” yaklaşımındayım; Redis’i taktiksel, veritabanını stratejik düşünürüm.
Kalıcı hacimler, sadece veri güveni değil, yedek akışının basitleşmesi demek. Çünkü “al şu klasörü ve şu veritabanını yedekle” talimatı birkaç satırlık bir script’e dönüşür. Mesela şöyle düşünün: wp-content’i sıkıştır, veritabanını dışa aktar, ikisini birlikte sakla. Geri yüklemek de aynı kadar düz, bu kadar.
docker-compose.yml: Çalışır Bir Örnek ve Küçük Sırlar
Compose Dosyası
Aşağıdaki örnek, tek bir klasörden ayağa kalkacak temel bir düzen sunuyor. Nginx, WordPress (PHP-FPM), MariaDB ve Redis bir ağda buluşuyor; kalıcı hacimler bağlanıyor; ufak sağlık kontrolleri konuluyor. Üstüne bir de otomatik yedek için küçük bir servis hazırlıyoruz.
version: "3.9"
services:
db:
image: mariadb:10.11
container_name: wp_db
command: ["--skip-log-bin", "--innodb-buffer-pool-size=256M"]
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", "mysqladmin", "ping", "-h", "127.0.0.1"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: wp_redis
command: ["redis-server", "--appendonly", "yes", "--save", "60", "1000"]
volumes:
- redis_data:/data
restart: unless-stopped
wordpress:
image: wordpress:php8.2-fpm
container_name: wp_php
environment:
- WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_NAME=${DB_NAME}
- WORDPRESS_DB_USER=${DB_USER}
- WORDPRESS_DB_PASSWORD=${DB_PASSWORD}
- WORDPRESS_CONFIG_EXTRA=define('WP_REDIS_HOST','redis');n define('WP_MEMORY_LIMIT','256M');n define('AUTOMATIC_UPDATER_DISABLED', false);
volumes:
- wp_data:/var/www/html
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
nginx:
image: nginx:alpine
container_name: wp_nginx
ports:
- "80:80"
volumes:
- wp_data:/var/www/html:ro
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- nginx_logs:/var/log/nginx
depends_on:
- wordpress
restart: unless-stopped
backup:
image: alpine:3.20
container_name: wp_backup
entrypoint: ["/bin/sh","-c"]
command: |
apk add --no-cache bash mariadb-client restic tzdata ca-certificates tar pigz &&
mkdir -p /scripts &&
echo "0 3 * * * /bin/bash /scripts/backup.sh >> /var/log/backup.log 2>&1" > /etc/crontabs/root &&
crond -f -L /var/log/cron.log
environment:
- RESTIC_REPOSITORY=${RESTIC_REPOSITORY}
- RESTIC_PASSWORD=${RESTIC_PASSWORD}
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
- TZ=Europe/Istanbul
- DB_HOST=db
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=${DB_NAME}
volumes:
- wp_data:/var/www/html:ro
- ./backup:/scripts
depends_on:
db:
condition: service_healthy
restart: unless-stopped
volumes:
wp_data:
db_data:
redis_data:
nginx_logs:
.env Dosyası
Aynı klasörde basit bir .env dosyası, parolaları ve isimleri ortam değişkeni olarak verir. Kodun gövdesinde gizlemekten iyidir. İsterseniz Docker secrets veya kasada saklama yöntemleriyle daha da sertleştirebilirsiniz.
DB_NAME=wordpress
DB_USER=wpuser
DB_PASSWORD=degistir-lutfen
DB_ROOT_PASSWORD=degistir-ama-sakince
RESTIC_REPOSITORY=s3:s3.amazonaws.com/sizin-bucketiniz/wordpress-yedek
RESTIC_PASSWORD=uzun-bir-sifre-secin
AWS_ACCESS_KEY_ID=AKIAXXXXX
AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxx
Yedek Script’i
Geceleri saat 03:00’te çalışacak minik bir script. Veritabanını dışa aktarır, wp-content’i bir arşive alır, ikisini restic ile uzak depoya yollar. Geri dönüş yolunu da hazırladık, birazdan anlatacağım.
# backup/backup.sh
#!/usr/bin/env bash
set -euo pipefail
STAMP=$(date +"%Y%m%d-%H%M%S")
WORK=/tmp/backup-${STAMP}
mkdir -p "$WORK"
# 1) Veritabanı dump
mysqldump -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD" "$DB_NAME"
--single-transaction --routines --events --triggers
| pigz -9 > "$WORK/db-${STAMP}.sql.gz"
# 2) wp-content arşivi (çekirdek ve eklentiler aynı volume'da, isterseniz tümünü alın)
cd /var/www/html
if [ -d wp-content ]; then
tar -I "pigz -9" -cf "$WORK/wp-content-${STAMP}.tar.gz" wp-content
else
echo "wp-content yok gibi, yine de devam" >&2
fi
# 3) restic init (ilkse) ve yedekleme
restic snapshots || restic init
restic backup --host wordpress
"$WORK/db-${STAMP}.sql.gz"
"$WORK/wp-content-${STAMP}.tar.gz" || true
# 4) Eski yedekleri budama: 7 günlük, 4 haftalık, 6 aylık
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
rm -rf "$WORK"
echo "Yedek tamam: ${STAMP}"
İlk Çalıştırma
Komutlar sade. Önce klasör yapısını hazırlayın: nginx dizini, backup dizini, .env dosyası. Sonra çalıştırın ve çayı koyun.
docker compose pull
docker compose up -d
İlk kurulumda WordPress sizden site adı ve yönetici hesabı isteyecek. Nginx ayarlarını da ekleyelim ki vitrinimiz derli toplu olsun.
Nginx ve Redis: Vitrini Parlatmak, İçeriği Hızlandırmak
Nginx Yapılandırması
Basit ama iş gören bir Nginx, trafiği WordPress’in PHP-FPM’ine aktarır, statik dosyaları hızlı yeniler ve gereksiz yükü azaltır. Aşağıdaki iki dosya ile başlamak rahatlatır.
# nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
client_max_body_size 64m;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
include /etc/nginx/conf.d/*.conf;
}
# nginx/conf.d/site.conf
upstream php {
server wordpress:9000;
keepalive 16;
}
server {
listen 80;
server_name _;
root /var/www/html;
index index.php index.html;
location ~* .(png|jpg|jpeg|gif|svg|webp|css|js|ico|woff|woff2)$ {
expires 7d;
add_header Cache-Control "public, max-age=604800";
try_files $uri $uri/ =404;
}
location /wp-content/cache/ { # plugin cache klasörleri olursa
expires 10m;
add_header Cache-Control "public, max-age=600";
}
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ .php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass php;
fastcgi_read_timeout 120s;
}
location ~* /(.git|.env|wp-config.php|readme.html|license.txt) {
deny all;
}
}
Üstteki yapı, yeni başlayanlar için yeterli bir zemin. Zamanla mikro önbellekleme ya da ek güvenlik başlıkları eklemek isteyebilirsiniz. Daha derine inmek isterseniz, sitemizdeki Docker ile WordPress’i VPS’te yaşatmanın sıcak hikayesi bu düzenin akrabası gibi; pratik dokunuşlarla çok şey netleşiyor.
Redis’i WordPress’e Tanıtmak
WordPress ile Redis konuşsun diye genelde küçük bir eklenti yeter. “Redis Object Cache” gibi popüler olanları kurup etkinleştirin. Ayarlarda “host” olarak redis konteyner adını yazmanız genelde yetiyor. Biz zaten WORDPRESS_CONFIG_EXTRA ile ‘WP_REDIS_HOST’ tanımladık. Aktivasyon sonrası yönetim panelinde “Connected” görürseniz işler yolunda demektir. Bazen sayfa oluşturucular veya ağır eklentiler ilk yüklemede üşengeç davranır, o anlarda önbelleği bir kere temizlemek rahatlatır.
Otomatik Yedek ve Geri Yükleme ile Güncelleme Akışı: Önce Emniyet, Sonra Cesaret
Yedek Akışı
Hazırladığımız backup servisi her gece 03:00’te çalışıyor. Veritabanı dump alınıyor, wp-content arşivleniyor, restic ile uzak depoya gönderiliyor. Bu uzak depo bir S3 uyumlu nesne depolama olabilir. Restic’in incelikleri için restic ile yedekleri S3’e göndermek belgesine göz atmak iyi gelir. Benim sahada en sevdiğim detay, “forget/prune” ile arşivi yaşlandırabilmek; dünün, geçen haftanın ve aylık fotoğraflarınız birlikte duruyor.
Geri yükleme provası baştan yapılınca, kriz anında titreme olmuyor. Provayı şöyle düşünebilirsiniz: Temiz bir klasöre aynı Compose’u kurup, yedekten çekiyorsunuz. İlk geri yüklemeyi görmeden “tamam yedekliyiz” demek havada kalır. Ben provaları küçük bir alt ortamda yapmayı seviyorum; verinin bir kısmıyla bile olsa kas hafızası kazanılıyor.
Geri Yükleme Komutları
Prova zamanı geldiğinde, backup konteynerine girip restic ile dosyaları indirirsiniz. Ardından veritabanını içe aktarır, wp-content’i geri açarsınız.
# son snapshot'ı listeleyin
docker exec -it wp_backup restic snapshots
# istediğiniz anın arşivini bir klasöre çıkarın
RESTORE=/tmp/restore-$(date +%s)
docker exec -e RESTORE=${RESTORE} -it wp_backup
sh -lc "mkdir -p $RESTORE && restic restore latest --target $RESTORE"
# wp-content'i geri koymak (nginx/wordpress durdurmayı unutmayın)
docker compose stop nginx wordpress
HOST_RESTORE=$(docker exec -it wp_backup sh -lc "echo $RESTORE" | tr -d 'r')
# Host'a kopyalamak için (Linux/Mac):
# docker cp wp_backup:${HOST_RESTORE}/tmp/backup-*/wp-content-*.tar.gz ./
# sonra uygun klasöre açıp wp_data volume'a yerleştirin
docker compose start db
# Veritabanı geri yükleme (dosyayı host'a alıp içe aktarın)
gzip -dc db-YYYYMMDD-HHMMSS.sql.gz | docker exec -i wp_db sh -lc
"mysql -u$DB_USER -p$DB_PASSWORD $DB_NAME"
docker compose start wordpress nginx
Buradaki adımların bir kez işlediğini görmek, akşamları çok daha rahat uyutuyor. Küçük bir not: İçerik canlıyken geri yükleme yapılacaksa, “önce bakım sayfası, sonra işlem” sırasını bozmamak lazım.
Güncelleme Akışı
Güncelleme en çok yanlış anlaşılan konu. Aslında üç parçadan oluşur: “önce yedek”, “sonra imajları güncelle”, “en sonda WordPress çekirdeği/eklenti/tema”. Benim pratik akışım şöyle akıyor: Önce bir manuel yedek tetikleyip tamamlandığını görüyorum. Sonra Compose imajlarını çekiyorum. Ardından WordPress’i WP-CLI ile güncelliyorum. Her adım küçük, ama bir araya gelince sağlam.
WP-CLI ile Güncelleme
WP-CLI, WordPress yönetiminde komut satırını sevmek için güçlü bir bahane. Kurulumla uğraşmadan bir konteyner olarak çağırabiliriz. Resmi dökümana göz atmak isterseniz WP-CLI komutları başlığını seversiniz.
# Tek seferlik WP-CLI konteyneri ile komut örnekleri
# Çekirdeği minör güncelle: (temkinli)
docker run --rm -it
--network $(docker network ls --filter name=_default --format {{.Name}})
-v $(pwd)/html:/var/www/html
-e WORDPRESS_DB_HOST=db:3306
-e WORDPRESS_DB_NAME=${DB_NAME}
-e WORDPRESS_DB_USER=${DB_USER}
-e WORDPRESS_DB_PASSWORD=${DB_PASSWORD}
wordpress:cli-php8.2
wp core update --minor --path=/var/www/html
# Eklentileri güncelle
docker run --rm -it
--network $(docker network ls --filter name=_default --format {{.Name}})
-v $(pwd)/html:/var/www/html
-e WORDPRESS_DB_HOST=db:3306
-e WORDPRESS_DB_NAME=${DB_NAME}
-e WORDPRESS_DB_USER=${DB_USER}
-e WORDPRESS_DB_PASSWORD=${DB_PASSWORD}
wordpress:cli-php8.2
wp plugin update --all --path=/var/www/html
Üstteki örnekler, WordPress dosyalarının host’ta ./html altında olduğu bir düzeni varsayıyor. Biz named volume kullandık. Bu durumda en kolayı, kısa süreliğine volume’ı bir yardımcı konteynere mount edip /var/www/html’i o konteynerden kullanmak. Alternatif olarak WordPress servisinde WP-CLI çalıştırmak da mümkün. Yani yöntem çok; önemli olan, önce yedek, sonra güncelle prensibini bozmamak.
Uygulama İmajlarını Güncellemek
Compose tarafında güncelleme genellikle iki komutla biter. Önce yeni imajları çekersiniz, sonra konteynerleri yeniden yaratırsınız. Her şey named volume’larda durduğu için veri yerinden kıpırdamaz.
docker compose pull
docker compose up -d
Nginx, PHP-FPM veya Redis imajlarındaki güncellemeleri böylece almış olursunuz. Büyük versiyon geçişlerinde küçük bir test ortamı kurmak iç rahatlatır. Aynı Compose dosyasını başka bir ağ ve port ile ayağa kaldırıp göz atmak, sürprizleri azaltır.
İnce Ayarlar ve Küçük Taktikler
Güncelleme öncesi “bakım modunu” kısa bir eklentiyle ya da Nginx’te basit bir kural ile açıp, işlem biter bitmez kapatmak işi pürüzsüz yapar. Redis önbelleğini güncellemelerden sonra temizlemek çoğu sitede “eski parça kalmış sanki” duygusunu yok eder. Ve tabii ki otomatik yedek akışını bir healthcheck gibi düşünün; yedeklerin boyutu, sayısı ve geri dönüş testi takvimini bir not defterine yazıp, ay sonunda bir bakış atın.
Compose Referanslarına Bakmak İyi Gelir
İşin tabanı Compose olduğu için, yeni bir özellik veya ufak bir püf aradığınızda Docker Compose referansları hayat kurtarıyor. Orada gördüğünüz bir ayarı küçük küçük denemek, bu yapıyı tam size göre hale getiriyor.
Kapanış: Bir Kez Kur, Hep Güven
Toparlayalım. WordPress’i Docker Compose ile Nginx, MariaDB ve Redis’in arasına yerleştirdiğimizde, aslında bir düzen kurduk. Parçalar ayrı; dokunurken birbirini rahatsız etmiyorlar. Kalıcı hacimler sayesinde verinin omurgası sağlam. Otomatik yedek akışı her gece bir fotoğraf çekiyor, siz de rahat uyuyorsunuz. Güncelleme akışı ise cesaret veriyor; önce emniyet kemeri, sonra yolculuk.
Pratik bir öneri seti bırakayım: İlk haftayı gözlem haftası yapın. Log’lara arada bir bakın, Redis bağlantısının gerçekten aktif olduğundan emin olun, yedeklerin gerçekten uzak depoya aktığını görün. Haftanın sonunda küçük bir geri yükleme provası yapın; birkaç adımı yeniden yaşamak kaslarınızı ısıtıyor. Ve unutmayın, küçük iterasyonlar büyük düzenlerin anahtarı. Bugün bir ayarı düzeltirsiniz, yarın imajı güncellersiniz, öbür gün yedeklerin saklama politikasını güzelleştirirsiniz.
Umarım bu rehber elinizi rahatlattı. Bir güncelleme sabahında yine buluşur, “bak bunu da ekledim, hayat kolaylaştı” deriz. Sorularınız olursa, üzerinde konuşmak her zaman keyif. Bir dahaki yazıda görüşmek üzere.
