WP-CLI ile Otomatik WordPress Yedekleme Scripti Yazımı

WordPress sitenizi yedeklemek için cPanel’e girip tıklamalar yapmaktan, FTP ile dosyaları indirmeye çalışmaktan bıktıysanız, bu yazı tam size göre. WP-CLI kullanarak hem veritabanını hem de dosyaları otomatik olarak yedekleyen, çalıştığında size bildirim gönderen ve eski yedekleri temizleyen bir script yazacağız. Bir kez kurarsınız, cron job’a bağlarsınız, gerisini unutursunuz.

WP-CLI Neden Yedekleme için İdeal?

WordPress yedeklemesini plugin ile yapmak çoğu zaman güvenilmez. Sunucunun PHP bellek limiti dolabilir, büyük sitelerde timeout alabilirsiniz, plugin yedekleri bazen bozuk çıkabilir. WP-CLI ise doğrudan komut satırından çalışır, PHP limitlerinden bağımsız hareket eder ve scriptlere entegre etmek son derece kolaydır.

WP-CLI’nin db export komutu, MySQL dump alırken WordPress yapılandırmasını doğrudan wp-config.php dosyasından okur. Yani veritabanı kullanıcı adı ve şifreyi ayrıca belirtmenize gerek kalmaz. Bu küçük detay aslında scriptleri çok daha taşınabilir yapar.

Script Mimarisi: Ne Yapacağız?

Yazacağımız yedekleme sistemi birkaç temel görev üstlenecek:

  • WordPress veritabanını sıkıştırılmış SQL dosyası olarak yedeklemek
  • WordPress dosyalarını (wp-content özelinde) tar arşivi olarak yedeklemek
  • Yedekleri tarih damgalı klasörlerde organize etmek
  • Belirlenen gün sayısından eski yedekleri otomatik silmek
  • İşlem sonunda e-posta veya Slack bildirimi göndermek
  • Log tutmak

Önce temel yapıyı kuralım, sonra parçaları birleştirelim.

Temel WP-CLI Komutlarına Hızlı Bakış

Script yazmaya başlamadan önce kullanacağımız WP-CLI komutlarını anlayalım.

Veritabanı yedekleme:

wp db export /yedek/dizini/veritabani.sql --path=/var/www/html

Sıkıştırılmış format için:

wp db export - --path=/var/www/html | gzip > /yedek/dizini/veritabani.sql.gz

Sadece belirli tabloları yedeklemek:

wp db export --tables=$(wp db tables --all-tables-with-prefix --format=csv --path=/var/www/html) /yedek/dizini/veritabani.sql --path=/var/www/html

WordPress kurulum bilgilerini kontrol etmek:

wp core version --path=/var/www/html
wp option get siteurl --path=/var/www/html

Bu son komut önemli çünkü script içinde sitenin URL’sini alıp log mesajlarında ve bildirim e-postalarında kullanacağız.

Ana Yedekleme Scriptini Yazalım

Şimdi gerçek script’e geçelim. Bu scripti /usr/local/bin/wp-backup.sh olarak kaydedeceğiz.

#!/bin/bash

# ============================================
# WordPress Otomatik Yedekleme Scripti
# Kullanim: ./wp-backup.sh /var/www/html
# ============================================

# --- YAPILANDIRMA ---
WP_PATH="${1:-/var/www/html}"
BACKUP_ROOT="/var/backups/wordpress"
LOG_FILE="/var/log/wp-backup.log"
RETENTION_DAYS=7
EMAIL_NOTIFY="[email protected]"
SLACK_WEBHOOK=""  # Slack kullanmak isterseniz webhook URL girin

# --- RENKLİ ÇIKTI ---
RED='33[0;31m'
GREEN='33[0;32m'
YELLOW='33[1;33m'
NC='33[0m'

# --- FONKSİYONLAR ---
log() {
    local seviye="$1"
    local mesaj="$2"
    local zaman=$(date '+%Y-%m-%d %H:%M:%S')
    echo -e "${zaman} [${seviye}] ${mesaj}" | tee -a "$LOG_FILE"
}

hata_kontrol() {
    if [ $? -ne 0 ]; then
        log "HATA" "$1"
        bildirim_gonder "HATA" "$1"
        exit 1
    fi
}

bildirim_gonder() {
    local durum="$1"
    local mesaj="$2"
    
    # E-posta bildirimi
    if command -v mail &> /dev/null && [ -n "$EMAIL_NOTIFY" ]; then
        echo "$mesaj" | mail -s "[WP Yedek] ${durum}: ${SITE_URL}" "$EMAIL_NOTIFY"
    fi
    
    # Slack bildirimi
    if [ -n "$SLACK_WEBHOOK" ]; then
        local renk="good"
        [ "$durum" = "HATA" ] && renk="danger"
        curl -s -X POST -H 'Content-type: application/json' 
            --data "{"attachments":[{"color":"${renk}","text":"${mesaj}"}]}" 
            "$SLACK_WEBHOOK" > /dev/null
    fi
}

# --- ÖN KONTROLLER ---
log "INFO" "Yedekleme işlemi başlatılıyor..."

# WP-CLI mevcut mu?
if ! command -v wp &> /dev/null; then
    log "HATA" "WP-CLI bulunamadı. Kurulum: https://wp-cli.org"
    exit 1
fi

# WordPress dizini geçerli mi?
if [ ! -f "${WP_PATH}/wp-config.php" ]; then
    log "HATA" "Geçerli bir WordPress kurulumu bulunamadı: ${WP_PATH}"
    exit 1
fi

# Site URL'sini al
SITE_URL=$(wp option get siteurl --path="$WP_PATH" 2>/dev/null)
log "INFO" "Site: ${SITE_URL}"

# --- BACKUP DİZİNİ ---
TARIH=$(date '+%Y-%m-%d_%H-%M-%S')
BACKUP_DIR="${BACKUP_ROOT}/${TARIH}"
mkdir -p "$BACKUP_DIR"
hata_kontrol "Yedek dizini oluşturulamadı: ${BACKUP_DIR}"

log "INFO" "Yedek dizini: ${BACKUP_DIR}"

# --- VERİTABANI YEDEĞİ ---
log "INFO" "Veritabanı yedekleniyor..."
DB_BACKUP="${BACKUP_DIR}/database.sql.gz"

wp db export - --path="$WP_PATH" | gzip > "$DB_BACKUP"
hata_kontrol "Veritabanı yedeklemesi başarısız oldu"

DB_BOYUT=$(du -sh "$DB_BACKUP" | cut -f1)
log "INFO" "Veritabanı yedeği tamamlandı. Boyut: ${DB_BOYUT}"

# --- DOSYA YEDEĞİ ---
log "INFO" "wp-content dizini yedekleniyor..."
FILES_BACKUP="${BACKUP_DIR}/wp-content.tar.gz"

tar -czf "$FILES_BACKUP" 
    --exclude="${WP_PATH}/wp-content/cache" 
    --exclude="${WP_PATH}/wp-content/upgrade" 
    -C "$WP_PATH" wp-content

hata_kontrol "Dosya yedeklemesi başarısız oldu"

FILES_BOYUT=$(du -sh "$FILES_BACKUP" | cut -f1)
log "INFO" "Dosya yedeği tamamlandı. Boyut: ${FILES_BOYUT}"

# --- TOPLAM BOYUT ---
TOPLAM_BOYUT=$(du -sh "$BACKUP_DIR" | cut -f1)
log "INFO" "Toplam yedek boyutu: ${TOPLAM_BOYUT}"

# --- ESKİ YEDEKLERİ TEMİZLE ---
log "INFO" "${RETENTION_DAYS} günden eski yedekler temizleniyor..."
find "$BACKUP_ROOT" -maxdepth 1 -type d -mtime "+${RETENTION_DAYS}" -exec rm -rf {} ;
log "INFO" "Temizlik tamamlandı"

# --- BAŞARILI BİLDİRİM ---
MESAJ="Yedekleme başarıyla tamamlandı.nSite: ${SITE_URL}nYedek: ${BACKUP_DIR}nVT: ${DB_BOYUT} | Dosyalar: ${FILES_BOYUT} | Toplam: ${TOPLAM_BOYUT}"
log "BASARI" "Yedekleme tamamlandı!"
bildirim_gonder "BASARI" "$MESAJ"

exit 0

Script’i çalıştırılabilir yapın:

chmod +x /usr/local/bin/wp-backup.sh

Çoklu Site Desteği Ekleyelim

Eğer sunucunuzda birden fazla WordPress sitesi çalışıyorsa, her biri için ayrı ayrı cron job tanımlamak yerine tek bir wrapper script kullanabilirsiniz.

#!/bin/bash

# /usr/local/bin/wp-backup-all.sh
# Sunucudaki tüm WordPress sitelerini yedekler

SITES_FILE="/etc/wp-sites.list"
LOG_FILE="/var/log/wp-backup-all.log"

if [ ! -f "$SITES_FILE" ]; then
    echo "Site listesi bulunamadı: $SITES_FILE"
    exit 1
fi

BASARILI=0
BASARISIZ=0

while IFS= read -r site_path; do
    # Boş satır ve yorum satırlarını atla
    [[ -z "$site_path" || "$site_path" == #* ]] && continue
    
    echo "$(date '+%Y-%m-%d %H:%M:%S') Yedekleniyor: $site_path" >> "$LOG_FILE"
    
    if /usr/local/bin/wp-backup.sh "$site_path"; then
        BASARILI=$((BASARILI + 1))
    else
        BASARISIZ=$((BASARISIZ + 1))
        echo "$(date '+%Y-%m-%d %H:%M:%S') HATA: $site_path yedeklenemedi" >> "$LOG_FILE"
    fi
    
done < "$SITES_FILE"

echo "$(date '+%Y-%m-%d %H:%M:%S') Tamamlandı. Başarılı: $BASARILI | Başarısız: $BASARISIZ" >> "$LOG_FILE"

/etc/wp-sites.list dosyanızı şu şekilde oluşturun:

# WordPress site dizinleri
/var/www/html/site1
/var/www/html/site2
/home/kullanici/public_html
# /var/www/html/bakimda  <- Bu satır devre dışı

Sadece Veritabanı Yedeği Alan Hafif Script

Bazı durumlarda sadece veritabanını sık sık yedeklemek istersiniz; dosyalar o kadar sık değişmez ama veritabanı sürekli değişir. İşte buna özel hafif bir versiyon:

#!/bin/bash

# /usr/local/bin/wp-db-backup.sh
# Saatlik veya daha sık çalıştırmak için optimize edilmiş DB yedeği

WP_PATH="${1:-/var/www/html}"
DB_BACKUP_ROOT="/var/backups/wordpress-db"
RETENTION_HOURS=48  # Kaç saatlik yedeği tut

# Dizin oluştur
mkdir -p "$DB_BACKUP_ROOT"

# Dosya adı: sitenin adı + tarih damgası
SITE_ADI=$(basename "$WP_PATH")
TARIH=$(date '+%Y%m%d_%H%M%S')
DB_FILE="${DB_BACKUP_ROOT}/${SITE_ADI}_${TARIH}.sql.gz"

# WP-CLI ile export al, gzip ile sıkıştır
wp db export - 
    --path="$WP_PATH" 
    --default-character-set=utf8mb4 
    2>/dev/null | gzip -9 > "$DB_FILE"

if [ $? -eq 0 ]; then
    echo "$(date): DB yedeği alındı -> $(du -sh $DB_FILE | cut -f1) -> $DB_FILE"
else
    echo "$(date): HATA - DB yedeği alınamadı: $WP_PATH" >&2
    rm -f "$DB_FILE"
    exit 1
fi

# 48 saatten eski DB yedeklerini sil
find "$DB_BACKUP_ROOT" -name "${SITE_ADI}_*.sql.gz" -mmin "+$((RETENTION_HOURS * 60))" -delete

exit 0

Cron Job Kurulumu

Scriptleri otomatik çalıştırmak için cron job’ları ayarlayalım:

# crontab -e komutuyla açın

# Her gece 02:30'da tam yedek
30 2 * * * /usr/local/bin/wp-backup.sh /var/www/html >> /var/log/wp-backup-cron.log 2>&1

# Her 4 saatte bir sadece veritabanı yedeği
0 */4 * * * /usr/local/bin/wp-db-backup.sh /var/www/html >> /var/log/wp-db-backup-cron.log 2>&1

# Çoklu site için her gece 03:00'da
0 3 * * * /usr/local/bin/wp-backup-all.sh >> /var/log/wp-backup-all-cron.log 2>&1

Cron job’ın çalıştığı kullanıcının WP-CLI’ya ve WordPress dosyalarına erişimi olmalı. Genellikle www-data veya nginx kullanıcısı olur:

# www-data kullanıcısı için cron
sudo -u www-data crontab -e

# Veya root olarak çalışıyorsa WP-CLI'yı --allow-root ile çağırın
# wp db export --allow-root ...

Yedek Doğrulama: Aldığın Yedek Gerçekten Çalışıyor mu?

Yedek almak tek başına yeterli değil; o yedeğin geri yüklenebildiğinden emin olmak gerekir. Bu basit doğrulama scripti alınan SQL dosyasının bozuk olup olmadığını test eder:

#!/bin/bash

# /usr/local/bin/wp-backup-verify.sh
# Son yedeği doğrular

WP_PATH="${1:-/var/www/html}"
BACKUP_ROOT="/var/backups/wordpress"

# En son yedek dizinini bul
SON_YEDEK=$(ls -1t "$BACKUP_ROOT" | head -1)

if [ -z "$SON_YEDEK" ]; then
    echo "HATA: Hiç yedek bulunamadı"
    exit 1
fi

BACKUP_DIR="${BACKUP_ROOT}/${SON_YEDEK}"
DB_FILE="${BACKUP_DIR}/database.sql.gz"
CONTENT_FILE="${BACKUP_DIR}/wp-content.tar.gz"

echo "Doğrulanıyor: $BACKUP_DIR"

# Gzip bütünlüğünü kontrol et
echo -n "Veritabanı dosyası kontrolü... "
if gzip -t "$DB_FILE" 2>/dev/null; then
    echo "OK"
else
    echo "BOZUK!"
    exit 1
fi

# SQL içeriğini kontrol et
echo -n "SQL içeriği kontrolü... "
TABLO_SAYISI=$(zcat "$DB_FILE" | grep -c "CREATE TABLE")
if [ "$TABLO_SAYISI" -gt 0 ]; then
    echo "OK (${TABLO_SAYISI} tablo bulundu)"
else
    echo "ŞÜPHELI - Tablo bulunamadı"
fi

# Tar arşivini kontrol et
echo -n "Dosya arşivi kontrolü... "
if tar -tzf "$CONTENT_FILE" > /dev/null 2>&1; then
    DOSYA_SAYISI=$(tar -tzf "$CONTENT_FILE" | wc -l)
    echo "OK (${DOSYA_SAYISI} dosya)"
else
    echo "BOZUK!"
    exit 1
fi

# wp-config.php'nin wp-content yedeğinde olmadığını kontrol et (güvenlik)
if tar -tzf "$CONTENT_FILE" | grep -q "wp-config.php"; then
    echo "UYARI: wp-config.php arşivde mevcut, hassas bilgi içerebilir!"
fi

echo ""
echo "Doğrulama tamamlandı. Yedek: $BACKUP_DIR"

exit 0

Uzak Sunucuya Yedek Kopyalama

Yedekleri aynı sunucuda tutmak tam anlamıyla yedek değil. Sunucu çöktüğünde yedekleriniz de gider. SCP veya rsync ile uzak sunucuya kopyalayalım:

# wp-backup.sh scriptinizin sonuna ekleyin

# --- UZAK SUNUCUYA KOPYALA ---
UZAK_SUNUCU="yedek-sunucu.sirketiniz.com"
UZAK_KULLANICI="backup-user"
UZAK_DIZIN="/var/backups/wp-remote"

if [ -n "$UZAK_SUNUCU" ]; then
    log "INFO" "Uzak sunucuya kopyalanıyor: ${UZAK_SUNUCU}"
    
    rsync -az --delete 
        -e "ssh -i /root/.ssh/backup_key -o StrictHostKeyChecking=no" 
        "${BACKUP_DIR}/" 
        "${UZAK_KULLANICI}@${UZAK_SUNUCU}:${UZAK_DIZIN}/${TARIH}/"
    
    if [ $? -eq 0 ]; then
        log "INFO" "Uzak kopyalama başarılı"
    else
        log "UYARI" "Uzak kopyalama başarısız, yerel yedek mevcut"
    fi
fi

SSH key’i ayrıca oluşturmanız gerekir:

# Backup için özel SSH key oluştur
ssh-keygen -t ed25519 -f /root/.ssh/backup_key -N ""

# Public key'i uzak sunucuya kopyala
ssh-copy-id -i /root/.ssh/backup_key.pub [email protected]

WooCommerce Siteniz Varsa Dikkat Edilecekler

WooCommerce kullanan sitelerde yedekleme yaparken birkaç önemli nokta var.

Büyük sipariş tabloları: WooCommerce’in wp_wc_orders ve eski sitelerde wp_posts tablosu binlerce sipariş kaydı tutabilir. Export işlemi uzun sürebilir. Bu durum için timeout’ları artırın:

# WP-CLI'nin bağlantı zaman aşımı olmadan çalışması için
WP_CLI_PHP_ARGS="-d max_execution_time=0" wp db export - --path="$WP_PATH" | gzip > "$DB_BACKUP"

Müşteri verileri ve KVKK: Eğer yedekleri uzak bir lokasyona kopyalıyorsanız müşteri kişisel verilerinin şifrelenmeden aktarılmadığından emin olun. SSH ile aktarım zaten şifreli gelir, ama yerel diskte oturan yedekler için de bir ek koruma düşünebilirsiniz:

# GPG ile yedek şifreleme
gpg --recipient [email protected] --encrypt "$DB_FILE"
rm "$DB_FILE"  # Şifresiz versiyonu sil

Script Bakımı ve İzleme

Yedekleme scriptinizi kurduktan sonra düzenli olarak kontrol etmek gerekir. Bunun için basit bir izleme scripti işe yarar:

# /usr/local/bin/wp-backup-check.sh
# Yedeklerin düzenli alınıp alınmadığını kontrol eder

BACKUP_ROOT="/var/backups/wordpress"
MAX_SURE=86400  # 24 saat (saniye cinsinden)
EMAIL="[email protected]"

SON_YEDEK_ZAMANI=$(find "$BACKUP_ROOT" -maxdepth 1 -type d -not -name "wordpress" | xargs ls -dt | head -1 | xargs stat -c %Y 2>/dev/null)

if [ -z "$SON_YEDEK_ZAMANI" ]; then
    echo "KRITIK: Hiç yedek bulunamadı!" | mail -s "[ALARM] WP Yedek Yok" "$EMAIL"
    exit 1
fi

SIMDI=$(date +%s)
GECEN=$((SIMDI - SON_YEDEK_ZAMANI))

if [ "$GECEN" -gt "$MAX_SURE" ]; then
    SAAT=$((GECEN / 3600))
    echo "Son yedek ${SAAT} saat önce alındı. Yedekleme sistemi sorunlu olabilir!" 
        | mail -s "[ALARM] WP Yedek Gecikmesi" "$EMAIL"
    exit 1
fi

echo "Yedekleme sistemi normal çalışıyor. Son yedek: $(date -d @$SON_YEDEK_ZAMANI)"
exit 0

Bu kontrol scriptini de cron’a ekleyin:

# Her sabah 08:00'de yedek durumunu kontrol et
0 8 * * * /usr/local/bin/wp-backup-check.sh

Sonuç

WP-CLI tabanlı bu yedekleme sistemi kurulumu biraz zaman alıyor ama bir kez çalışır hale getirince kendinizi çok daha güvende hissediyorsunuz. Sistemin güçlü yönleri şunlar:

  • Plugin bağımlılığı yok, PHP bellek limiti sorunu yok
  • Tarih damgalı organize yedek yapısı
  • Otomatik eski yedek temizleme ile disk taşması riski yok
  • Log mekanizması sayesinde sorun anında ne olduğunu anlama imkanı
  • Hem tek site hem çoklu site desteği
  • Yedek doğrulama ile kör güven yerine gerçek güvence

En önemli tavsiyem şu: Scripti kurun, bir kez manuel çalıştırın, üretilen yedekten gerçekten geri yükleme yapın. Geri yükleyemediğiniz bir yedek, hiç yedek alınmamasıyla aynı anlama gelir. Ayda bir de olsa geri yükleme testi yapın; aksi halde asıl ihtiyaç anında sürprizlerle karşılaşırsınız.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir