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.
