Uptime Kuma Veri Yedekleme ve Geri Yükleme

Uptime Kuma’yı production ortamında kullanan herkes er ya da geç şu soruyla karşılaşır: “Bu monitör ayarlarını ve geçmiş verilerini kaybedersem ne yaparım?” Özellikle onlarca monitör tanımladıktan, bildirim kanallarını yapılandırdıktan ve haftalarca uptime verisi biriktirdikten sonra bu sorunun cevabı kritik hale geliyor. Bu yazıda Uptime Kuma’nın veri yedekleme ve geri yükleme süreçlerini, kendi kurulumlarımda geliştirdiğim pratik yöntemlerle ele alacağım.

Uptime Kuma Neyi Nerede Saklar?

Herşeyden önce Uptime Kuma’nın veri mimarisini anlamak gerekiyor. Uygulama, tüm verilerini tek bir SQLite veritabanı dosyasında tutuyor. Bu dosya varsayılan olarak /app/data/kuma.db yolunda bulunuyor. Docker kurulumlarında ise bu dizini genellikle bir volume ile mount ediyoruz.

Yedekleme stratejisi belirlerken şu dosya ve dizinlere odaklanmak gerekiyor:

  • kuma.db: Ana veritabanı, tüm monitör tanımları, bildirim ayarları, kullanıcı bilgileri ve geçmiş uptime verileri burada
  • kuma.db-shm: SQLite shared memory dosyası, aktif yazma işlemleri sırasında oluşur
  • kuma.db-wal: Write-Ahead Log dosyası, henüz ana veritabanına işlenmemiş değişiklikler burada tutulur
  • ssl/: Eğer özel SSL sertifikası kullandıysanız bu dizini de dahil etmeniz gerekiyor
  • upload/: Özel ikonlar veya yüklenen dosyalar burada bulunuyor

Kritik nokta şu: WAL modunda çalışan SQLite veritabanlarını kopyalarken -shm ve -wal dosyalarını atlarsanız, yedeklediğinizi sandığınız veriler tutarsız veya eksik olabilir. Bunu bir sunucu göçü sırasında acı şekilde öğrendim.

Docker Kurulumunda Veri Dizinini Anlamak

Standart bir Docker Compose kurulumu şöyle görünüyor:

version: '3.8'
services:
  uptime-kuma:
    image: louislam/uptime-kuma:latest
    container_name: uptime-kuma
    volumes:
      - uptime-kuma-data:/app/data
    ports:
      - "3001:3001"
    restart: unless-stopped

volumes:
  uptime-kuma-data:

Bu kurulumda Docker managed volume kullanıyoruz. Volume’un fiziksel konumunu bulmak için:

docker volume inspect uptime-kuma-data

Bu komut bize şuna benzer bir çıktı verir:

[
    {
        "CreatedAt": "2024-01-15T08:23:45Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/uptime-kuma-data/_data",
        "Name": "uptime-kuma-data",
        "Options": null,
        "Scope": "local"
    }
]

Bazı kurulumlar bind mount tercih ediyor, yani doğrudan bir host dizinini bağlıyor:

volumes:
  - /opt/uptime-kuma/data:/app/data

Bu durumda yedekleme işlemi çok daha basit, çünkü dosyalara doğrudan erişilebiliyor.

Manuel Yedekleme Yöntemi

En basit yedekleme, container’ı durdurup dosyaları kopyalamak. Evet, çirkin ama güvenli:

# Container'ı durdur
docker stop uptime-kuma

# Yedek dizinini oluştur
BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p /opt/backups/uptime-kuma/$BACKUP_DATE

# Volume içeriğini kopyala
docker run --rm 
  -v uptime-kuma-data:/source 
  -v /opt/backups/uptime-kuma/$BACKUP_DATE:/backup 
  alpine sh -c "cp -rp /source/. /backup/"

# Container'ı tekrar başlat
docker start uptime-kuma

echo "Yedekleme tamamlandı: /opt/backups/uptime-kuma/$BACKUP_DATE"

Bu yöntem işe yarıyor ama her yedeklemede birkaç dakika downtime yaratıyor. Küçük ekipler için bu kabul edilebilir olabilir, ama 7/24 izleme sisteminizi durdurmak pek de hoş değil.

Canlı Yedekleme: SQLite .backup Komutu

SQLite’ın kendi .backup komutu, veritabanı açıkken bile tutarlı bir yedek almanıza olanak tanıyor. Bu yöntemi production’da tercih ediyorum:

# Container içinde SQLite backup komutu çalıştır
docker exec uptime-kuma sqlite3 /app/data/kuma.db ".backup /app/data/kuma_backup.db"

# Oluşturulan yedeği host'a kopyala
BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
docker cp uptime-kuma:/app/data/kuma_backup.db 
  /opt/backups/uptime-kuma/kuma_${BACKUP_DATE}.db

# Geçici backup dosyasını temizle
docker exec uptime-kuma rm /app/data/kuma_backup.db

Bu yöntem uygulama çalışırken bile tutarlı bir snapshot alıyor. SQLite’ın internal locking mekanizması sayesinde okuduğunuz veri tutarsız olmuyor.

Otomatik Yedekleme Script’i

Gerçek bir production ortamında manuel yedekleme yapmak olmaz. İşte düzenli olarak çalıştırdığım yedekleme script’i:

#!/bin/bash
# /usr/local/bin/backup-uptime-kuma.sh

set -euo pipefail

CONTAINER_NAME="uptime-kuma"
BACKUP_BASE_DIR="/opt/backups/uptime-kuma"
RETENTION_DAYS=30
LOG_FILE="/var/log/uptime-kuma-backup.log"

# Tarih damgası
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="${BACKUP_BASE_DIR}/${TIMESTAMP}"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# Container çalışıyor mu kontrol et
if ! docker inspect "$CONTAINER_NAME" &>/dev/null; then
    log "HATA: $CONTAINER_NAME container'i bulunamadi"
    exit 1
fi

log "Yedekleme basliyor: $TIMESTAMP"

# Yedek dizinini oluştur
mkdir -p "$BACKUP_DIR"

# SQLite canlı yedek al
log "SQLite backup aliniyor..."
docker exec "$CONTAINER_NAME" sqlite3 /app/data/kuma.db 
    ".backup /app/data/kuma_backup_temp.db"

# Veritabanını ve diğer dosyaları kopyala
log "Dosyalar kopyalaniyor..."
docker cp "${CONTAINER_NAME}:/app/data/kuma_backup_temp.db" 
    "${BACKUP_DIR}/kuma.db"

# SSL sertifikaları varsa kopyala
if docker exec "$CONTAINER_NAME" test -d /app/data/ssl 2>/dev/null; then
    docker cp "${CONTAINER_NAME}:/app/data/ssl" "${BACKUP_DIR}/"
    log "SSL sertifikalari kopyalandi"
fi

# Geçici dosyayı temizle
docker exec "$CONTAINER_NAME" rm -f /app/data/kuma_backup_temp.db

# Yedeği sıkıştır
log "Yedek sikistiriliyor..."
tar -czf "${BACKUP_BASE_DIR}/uptime_kuma_${TIMESTAMP}.tar.gz" 
    -C "$BACKUP_DIR" .

# Geçici dizini temizle
rm -rf "$BACKUP_DIR"

# Boyut bilgisi
BACKUP_SIZE=$(du -sh "${BACKUP_BASE_DIR}/uptime_kuma_${TIMESTAMP}.tar.gz" | cut -f1)
log "Yedekleme tamamlandi: uptime_kuma_${TIMESTAMP}.tar.gz ($BACKUP_SIZE)"

# Eski yedekleri temizle
log "Eski yedekler temizleniyor ($RETENTION_DAYS gun)..."
find "$BACKUP_BASE_DIR" -name "*.tar.gz" -mtime "+$RETENTION_DAYS" -delete
REMAINING=$(find "$BACKUP_BASE_DIR" -name "*.tar.gz" | wc -l)
log "Kalan yedek sayisi: $REMAINING"

log "Islem tamamlandi"

Script’i çalıştırılabilir yapıp cron’a ekleyin:

chmod +x /usr/local/bin/backup-uptime-kuma.sh

# Crontab'a ekle (her gece 02:00'de)
crontab -e
# Şu satırı ekleyin:
# 0 2 * * * /usr/local/bin/backup-uptime-kuma.sh

Uzak Sunucuya Yedek Aktarma

Yerel disk yeterli değil. Yedeklerinizi başka bir konuma aktarmanız gerekiyor. Ben genellikle iki yöntem kullanıyorum: rsync ile başka bir sunucuya veya rclone ile bir object storage’a.

rsync ile başka bir sunucuya gönderme:

#!/bin/bash
# Backup script'inin sonuna eklenecek

REMOTE_SERVER="backup-server.sirketiniz.com"
REMOTE_USER="backup"
REMOTE_DIR="/backups/uptime-kuma"
SSH_KEY="/root/.ssh/backup_key"

rsync -avz --delete 
    -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" 
    /opt/backups/uptime-kuma/ 
    "${REMOTE_USER}@${REMOTE_SERVER}:${REMOTE_DIR}/"

echo "Uzak sunucuya aktarim tamamlandi"

rclone ile S3 uyumlu object storage’a (Cloudflare R2, MinIO, Wasabi vb.) gönderme:

# rclone konfigürasyonu yapıldıktan sonra
rclone copy /opt/backups/uptime-kuma/ 
    minio-backup:uptime-kuma-backups/ 
    --include "*.tar.gz" 
    --min-age 1m 
    --log-level INFO

Geri Yükleme Süreci

Asıl test anı geldi. Yedekten geri yükleme, yedekleme kadar önemli, hatta daha önemli. Hiç test etmediğiniz bir yedek, olmayan yedekle aynı şeydir.

Senaryo 1: Mevcut container’a geri yükleme

Diyelim ki veritabanı bozuldu veya yanlışlıkla bir monitör konfigürasyonu silindi:

# Önce container'ı durdur
docker stop uptime-kuma

# Geri yüklenecek yedeği belirle
BACKUP_FILE="/opt/backups/uptime-kuma/uptime_kuma_20240115_020001.tar.gz"
RESTORE_TEMP="/tmp/uptime-kuma-restore"

# Yedeği geçici dizine aç
mkdir -p "$RESTORE_TEMP"
tar -xzf "$BACKUP_FILE" -C "$RESTORE_TEMP"

# Mevcut veritabanını sakla (güvenlik için)
docker cp uptime-kuma:/app/data/kuma.db 
    /tmp/kuma_before_restore_$(date +%Y%m%d%H%M%S).db

# Yedekten veritabanını geri yükle
docker cp "${RESTORE_TEMP}/kuma.db" uptime-kuma:/app/data/kuma.db

# Gerekirse SSL sertifikalarını da geri yükle
if [ -d "${RESTORE_TEMP}/ssl" ]; then
    docker cp "${RESTORE_TEMP}/ssl" uptime-kuma:/app/data/
fi

# İzin ayarlarını düzelt
docker exec uptime-kuma chown -R node:node /app/data

# Container'ı başlat
docker start uptime-kuma

# Geçici dosyaları temizle
rm -rf "$RESTORE_TEMP"

echo "Geri yukleme tamamlandi"

Senaryo 2: Yeni bir sunucuya taşıma

Sunucu göçü senaryosu, en yaygın karşılaştığım durum. Yeni sunucuda Docker kurulumunu yaptıktan sonra:

# Yeni sunucuda Docker Compose ile container'ı başlatmadan önce data dizinini hazırla
mkdir -p /opt/uptime-kuma/data

# Yedeği kopyala ve aç
scp backup-server:/backups/uptime-kuma/uptime_kuma_latest.tar.gz /tmp/
tar -xzf /tmp/uptime_kuma_latest.tar.gz -C /opt/uptime-kuma/data/

# İzin düzeltmesi (Uptime Kuma, node kullanıcısı olarak çalışıyor)
# Container içindeki UID genellikle 1000
chown -R 1000:1000 /opt/uptime-kuma/data/

# Docker Compose dosyasını bind mount kullanacak şekilde ayarla
cat > /opt/uptime-kuma/docker-compose.yml << 'EOF'
version: '3.8'
services:
  uptime-kuma:
    image: louislam/uptime-kuma:latest
    container_name: uptime-kuma
    volumes:
      - /opt/uptime-kuma/data:/app/data
    ports:
      - "3001:3001"
    restart: unless-stopped
EOF

# Container'ı başlat
cd /opt/uptime-kuma && docker compose up -d

# Logları kontrol et
docker logs uptime-kuma -f

Yedek Bütünlüğünü Doğrulama

Yedek aldım, iyi. Ama bu yedek gerçekten çalışıyor mu? Bütünlük kontrolü için birkaç yöntem:

#!/bin/bash
# Yedek doğrulama script'i

BACKUP_FILE="$1"

if [ -z "$BACKUP_FILE" ]; then
    echo "Kullanim: $0 <backup_dosyasi.tar.gz>"
    exit 1
fi

VERIFY_DIR="/tmp/uptime-kuma-verify-$$"
mkdir -p "$VERIFY_DIR"

echo "Arsiv butunlugu kontrol ediliyor..."
if ! tar -tzf "$BACKUP_FILE" &>/dev/null; then
    echo "HATA: Arsiv dosyasi bozuk!"
    rm -rf "$VERIFY_DIR"
    exit 1
fi

echo "Dosyalar aciliyor..."
tar -xzf "$BACKUP_FILE" -C "$VERIFY_DIR"

if [ ! -f "${VERIFY_DIR}/kuma.db" ]; then
    echo "HATA: kuma.db bulunamadi!"
    rm -rf "$VERIFY_DIR"
    exit 1
fi

echo "SQLite veritabani butunlugu kontrol ediliyor..."
if sqlite3 "${VERIFY_DIR}/kuma.db" "PRAGMA integrity_check;" | grep -q "ok"; then
    echo "OK: Veritabani butunlugu dogrulanadi"
else
    echo "HATA: Veritabani butunluk kontrolunden gecemedi!"
    rm -rf "$VERIFY_DIR"
    exit 1
fi

# Temel tablo varlıklarını kontrol et
MONITOR_COUNT=$(sqlite3 "${VERIFY_DIR}/kuma.db" 
    "SELECT COUNT(*) FROM monitor;" 2>/dev/null || echo "0")
echo "Monitör sayisi: $MONITOR_COUNT"

HEARTBEAT_COUNT=$(sqlite3 "${VERIFY_DIR}/kuma.db" 
    "SELECT COUNT(*) FROM heartbeat;" 2>/dev/null || echo "0")
echo "Heartbeat kaydi: $HEARTBEAT_COUNT"

rm -rf "$VERIFY_DIR"
echo "Dogrulama tamamlandi"

Bu script’i yedekleme script’inizin sonuna ekleyerek her yedeklemeden sonra otomatik doğrulama yapabilirsiniz.

Uptime Kuma Dahili Yedekleme Özelliği

Uptime Kuma’nın arayüzünde de basit bir yedekleme seçeneği var. Ayarlar > Hakkında bölümünde “Backup” düğmesiyle bir JSON dosyası indirebiliyorsunuz. Bu JSON dosyası monitör tanımlarınızı, bildirim ayarlarınızı ve durum sayfalarınızı içeriyor.

Bu yöntemin avantajları:

  • Kolay ve hızlı, teknik bilgi gerektirmiyor
  • JSON formatında okunabilir, elle düzenlenebilir
  • Platform bağımsız, farklı OS’lara taşıyabilirsiniz

Dezavantajları:

  • Geçmiş heartbeat verileri dahil değil
  • Otomatikleştirilmesi zor
  • Kullanıcı şifreleri ve bazı güvenlik bilgileri eksik olabilir

Bu arayüz yedeklemesini kullanmak istiyorsanız, API üzerinden de alabileceğinizi hatırlatayım. Ancak resmi API dokümantasyonu bu konuda oldukça sınırlı, bu yüzden direkt veritabanı yedeklemesini tercih ediyorum.

Felaket Kurtarma Tatbikatı

Teoride her şey güzel görünüyor. Ama gerçekten test etmeden bir yedekleme stratejisi benimsemek riskli. Ben her üç ayda bir şöyle bir tatbikat yapıyorum:

Yeni bir VM veya container ayağa kaldır, en son yedekten geri yükleme yap, tüm monitörlerin listelenip listelenmediğini kontrol et, bildirim kanallarının (Telegram, Slack vb.) yapılandırılmış olduğunu doğrula ve uygulamanın sorunsuz çalıştığından emin olduktan sonra test ortamını kaldır.

Bu tatbikat sayesinde gerçek bir felakette paniksiz şekilde hareket edebiliyorsunuz. Ayrıca geri yükleme sürenizi de ölçmüş oluyorsunuz; bu bilgi RTO (Recovery Time Objective) hesabı için önemli.

İzleme Sisteminin Kendisini İzlemek

İronik ama önemli: Yedekleme script’inin başarıyla çalışıp çalışmadığını da izlemeniz gerekiyor. Script başarısız olduğunda haberdar olmak için log dosyasını parse eden basit bir kontrol mekanizması:

#!/bin/bash
# Yedekleme durumunu kontrol eden script
# Uptime Kuma'daki bir "Push" monitörüne sinyal gönderir

PUSH_URL="https://uptime.sirketiniz.com/api/push/PUSH_TOKEN_BURAYA"
LOG_FILE="/var/log/uptime-kuma-backup.log"
MAX_AGE_HOURS=25

# Son başarılı yedeklemeden kaç saat geçti?
if [ -f "$LOG_FILE" ]; then
    LAST_SUCCESS=$(grep "Islem tamamlandi" "$LOG_FILE" | tail -1 | 
        awk '{print $1, $2}')
    
    if [ -n "$LAST_SUCCESS" ]; then
        LAST_EPOCH=$(date -d "$LAST_SUCCESS" +%s 2>/dev/null || 
            date -j -f "%Y-%m-%d %H:%M:%S" "$LAST_SUCCESS" +%s)
        NOW_EPOCH=$(date +%s)
        HOURS_AGO=$(( (NOW_EPOCH - LAST_EPOCH) / 3600 ))
        
        if [ "$HOURS_AGO" -lt "$MAX_AGE_HOURS" ]; then
            # Uptime Kuma Push monitörüne başarı sinyali gönder
            curl -s "${PUSH_URL}?status=up&msg=Backup+OK&ping=" > /dev/null
            echo "Yedekleme durumu: OK ($HOURS_AGO saat once)"
        else
            curl -s "${PUSH_URL}?status=down&msg=Backup+Failed" > /dev/null
            echo "UYARI: Son yedekleme $HOURS_AGO saat once!"
        fi
    fi
fi

Bu script’i de cron’a ekleyin, her saat çalıştırın. Uptime Kuma’da bir “Push” tipi monitör oluşturun ve sinyal gelmediğinde sizi uyarsın. Yedekleme sisteminizi izleme sisteminizle izliyorsunuz, kulağa biraz döngüsel geliyor ama bu yaklaşım gerçekten işe yarıyor.

Sonuç

Uptime Kuma yedekleme konusu başlangıçta göründüğünden biraz daha karmaşık, çünkü SQLite’ın WAL modunu anlamak ve canlı yedeklemenin nasıl çalıştığını kavramak gerekiyor. Ama bir kez kurduğunuzda gerçekten güvenilir bir yapı elde ediyorsunuz.

Özetlemek gerekirse: Günlük otomatik SQLite .backup komutuyla tutarlı yedek alın, bu yedekleri sıkıştırıp hem lokal hem uzak bir konuma gönderin, 30 gün saklayın ve düzenli olarak doğrulama yapın. Felaket kurtarma tatbikatını ihmal etmeyin, kağıt üzerinde mükemmel görünen planlar gerçek baskı altında çok farklı davranabilir.

En kötü senaryo her zaman aklınızın bir köşesinde durmalı: Sunucu tamamen el değiştirdi, veritabanı bozuldu, yanlışlıkla her şey silindi. Bu senaryolarda sizi kurtaracak olan, bugün kurduğunuz yedekleme rutini. Uptime Kuma’yı servisleri izlemek için kullanıyorsunuz; kendi izleme sisteminizin verilerini de aynı ciddiyetle koruyun.

Bir yanıt yazın

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