# Yedekleme ve geri yükleme

> QueryProxy kurulumunda nelerin yedekleneceği, SQLite'ın güvenle nasıl kopyalanacağı, Litestream ile çoğaltma ve APP_KEY'in saklanıp geri yüklenmesi.

- Dokümantasyon: Kılavuzlar
- Güncelleme: 2026-10-25
- Kaynak: https://queryproxy.com/tr/docs/yedekleme-ve-geri-yukleme/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
Bir QueryProxy yedeğinin birbirinden ayrıldığında işe yaramayan iki yarısı
vardır: **veri** (uygulama veritabanı ve sonuç dosyaları) ve içindeki saklanan
bağlantı kimlik bilgilerinin, chat secret'larının, maskeleme anahtarlarının ve
TLS sertifikalarının şifresini çözen **`APP_KEY`**. İkisini de yedekleyin ve
farklı yerlerde tutun.

## Nelerin yedekleneceği

| Ne | Nerede durur | Notlar |
| :-- | :-- | :-- |
| Uygulama veritabanı | Data volume'deki `database.sqlite` ya da `DB_*`'nin işaret ettiği MySQL, MariaDB veya PostgreSQL veritabanı | Kullanıcılar, takımlar, bağlantılar (şifreli), talepler, maskeleme kuralları, denetim günlüğü |
| `APP_KEY` | `APP_KEY` değişkeni, `APP_KEY_FILE` ya da data volume'deki üretilmiş `.app_key` | Onsuz şifreli sütunlar okunamaz. Bkz. [anahtar](#app_key) |
| Sonuç dosyaları | `QUERYPROXY_RESULT_DISK` diski; varsayılan olarak data volume'de `storage/app/private/results` | Maskelenmiş NDJSON dosyaları. `QUERYPROXY_RESULT_TTL_DAYS` sonra silinirler; bu yüzden birçok kurulum bunları atlar |
| Ortamınız | `-e` bayrakları, `--env-file` ya da compose dosyası | Aynı yapılandırmayı yeniden başlatmak için gerekir |

Data volume (`/var/www/html/storage/app`) SQLite veritabanını, üretilmiş anahtar
dosyasını ve sonuç dosyalarını tutar. `storage/app/tls` altındaki TLS dosyaları
çalışma anında veritabanındaki şifreli değerlerden yazılır; bu yüzden
veritabanıyla birlikte geri gelirler.

> **Anahtar volume'deyse yedek, anahtarı koruduğu verinin yanında taşır.**
> Arşivi yalnızca sahibinin okuyabileceği şekilde yazın (`umask 077`) ve onu
> anahtarın kendisi kadar özenle saklayın. Daha iyisi, `APP_KEY`'i volume'ün
> dışından verin; anahtar dosyası SQLite veritabanıyla aynı volume'ü
> paylaştığında `queryproxy:doctor` uyarır.

## SQLite veritabanını kopyalama

En basit yedek [README](https://github.com/QueryProxy/QueryProxy#upgrading)'deki
yedektir: konteyneri durdurun ve volume'ü arşivleyin. Konteyner durduğu için
hiçbir şey yazmaz; dolayısıyla kopya tutarlıdır.

Konteyner çalışırken veritabanını kopyalamak için, `sqlite3` komut satırı
aracı olan (QueryProxy image'ı bunu içermez) ve volume'ü okuyabilen bir
makineden SQLite'ın kendi `.backup` komutunu kullanın:

```bash
sqlite3 /path/to/database.sqlite ".backup '/backups/queryproxy-$(date +%F).sqlite'"
```

`.backup`, SQLite'ın online backup mekanizmasını kullanır. SQLite, online
backup sırasında diğer bağlantıların yazmaya devam edebileceğini ve başka bir
süreçten gelen bir yazmanın genellikle kopyayı yeniden başlattığını belgeler;
sürekli yoğun yazma altında bir kopya hiç bitmeyebilir. Bir QueryProxy kurulumu
az yazar, bu yüzden bu nadiren önemli olur; yine de komutun çıkış kodunun 0
olduğunu kontrol edin. QueryProxy çalışırken `database.sqlite`'ı `cp` ya da
`tar` ile kopyalamayın: dosya yazılmanın ortasında olabilir ve journal ya da WAL
yan dosyaları (`-journal`, `-wal`, `-shm`; son ikisi Litestream veritabanını WAL'a
geçirdikten sonra) ana dosyanın içermediği değişiklikleri taşıyabilir.

MySQL, MariaDB ve PostgreSQL için, her uygulama veritabanında olduğu gibi
veritabanının kendi araçlarını (`mysqldump`, `pg_dump` ya da snapshot'lar)
kullanın.

## Litestream ile sürekli çoğaltma

Gecelik bir kopya yerine dakikalar öncesine ait bir geri yükleme noktası
istiyorsanız, [Litestream](https://litestream.io/) bir SQLite veritabanını S3
gibi bir nesne depolamasına çoğaltır. QueryProxy'nin parçası değildir ve
QueryProxy test paketi onu çalıştırmaz; bu yüzden ona güvenmeden önce bir
staging kopyasında geri yüklemeyi prova edin.

Litestream yalnızca SQLite'ın WAL journal moduyla çalışır ve uygulama
geçirmediyse veritabanını buna geçirir. Checkpoint yaparken kısa yazma kilitleri
alır. Veritabanı başına bir Litestream süreci çalıştırın ve başka hiçbir
kurulumun kullanmadığı bir yola çoğaltın: belgeleri, birden çok uygulamanın
aynı bucket ve yola çoğaltmasının geri yükleyememenize yol açabileceği
konusunda uyarır.

Asgari bir yapılandırma:

```yaml
dbs:
  - path: /var/www/html/storage/app/database.sqlite
    replica:
      type: s3
      bucket: my-queryproxy-backups
      path: queryproxy
      region: eu-central-1
```

Litestream `AWS_ACCESS_KEY_ID` ve `AWS_SECRET_ACCESS_KEY` değerlerini ortamdan
okur. Aynı data volume'ü bağlayan bir sidecar olarak çalıştırın. Geri yükleme
komutu replica URL'sini ve bir çıktı yolunu alır:

```bash
litestream restore -o /restore/database.sqlite s3://my-queryproxy-backups/queryproxy
```

Litestream yalnızca veritabanını çoğaltır. `APP_KEY` ve sonuç dosyaları bunun
içinde değildir.

## APP_KEY

Anahtara kurulumun en değerli secret'ı gibi davranın. Her hedef veritabanı
kimlik bilgisini, chat imzalama secret'larını, takım başına maskeleme
anahtarlarını ve saklanan TLS sertifikalarını şifreler. Kaybolmuş bir anahtar
geri getirilemez ve özgün anahtarı olmadan başlatılan bir konteyner yenisini
üretir ve bunların hiçbirini okuyamaz.

İşe yarayan bir emanet (escrow) prosedürü:

1. **Bir kez, açıkça üretin** ve data volume'ün dışında tutun:
   `docker run --rm queryproxy/queryproxy php artisan key:generate --show`.
2. **Bir secret manager'da** ya da ikinci bir kişinin açabileceği mühürlü bir
   zarfta saklayın: en az iki sorumlu, iki ayrı yerde; hiçbiri yedek depolaması
   olmasın.
3. **`APP_KEY` ya da `APP_KEY_FILE` ile verin** (bir Docker ya da Kubernetes
   secret'ı), bkz. [Yapılandırma](/tr/docs/yapilandirma/#dosyadan-sırlar).
4. **Döndürmeleri kaydedin.** Döndürdüğünüzde eski anahtarı
   `APP_PREVIOUS_KEYS` içine taşıyın (virgülle ayrılmış); böylece eski şifreli
   veri hâlâ çözülür; ikisini de saklayın. Döndürmeden önce alınmış bir yedek,
   o sırada geçerli olan anahtara ihtiyaç duyar.
5. **Test edin.** Her döndürmeden ya da geri yüklemeden sonra
   `queryproxy:doctor`, anahtarın saklanan kimlik bilgilerinin şifresini çözüp
   çözemediğini bildirir (`app_key` kontrolüne bakın).

## Geri yükleme

1. Konteyneri durdurun ve kaldırın (`docker stop queryproxy && docker rm queryproxy`).
2. Veriyi geri koyun. README'deki gibi yapılmış bir arşiv için:

   > [!WARNING]
   > İkinci komut, yedeği açmadan önce `queryproxy-data` volume'ündeki her şeyi
   > siler. Önce arşivin istediğiniz arşiv olduğunu kontrol edin.

   ```bash
   docker run --rm -v queryproxy-data:/data -v "$PWD":/backup alpine \
     sh -c 'find /data -mindepth 1 -delete \
       && tar xzf /backup/queryproxy-data.tgz -C /data'
   ```

   SQLite için, çalıştırıyorsanız önce Litestream sidecar'ını durdurun:
   çalışan bir sidecar geri yüklenen dosyayı mevcut replica'nın üzerine
   çoğaltabilir. Ardından eski veritabanının yanındaki `database.sqlite-wal`,
   `database.sqlite-shm` ve `database.sqlite-journal` dosyalarını silin; SQLite
   artakalan bir WAL'ı geri yüklenen dosyaya uygular ve onu bozar. Bundan sonra
   bir `.backup` kopyasını `DB_DATABASE` yoluna volume'de `database.sqlite`
   olarak yerleştirin ya da `litestream restore -o <path>` komutunu henüz dosya
   bulunmayan bir yola çalıştırın (önce eski veritabanı dosyasını kaldırın).
   MySQL, MariaDB ve PostgreSQL için veritabanını kendi araçlarıyla geri
   yükleyin.
3. Yedeğin geldiği **aynı sürümü**, aynı ortam dosyasıyla ve **aynı `APP_KEY`**
   ile (döndürdüyseniz `APP_PREVIOUS_KEYS` ile birlikte) başlatın.
4. Sonucu kontrol edin:

   ```bash
   docker exec queryproxy php artisan queryproxy:doctor
   docker exec queryproxy php artisan audit:verify
   ```

   `app_key` `ok` olmalı ve `audit:verify` zincirin sağlam olduğunu
   bildirmelidir. Geri yüklenen bir kopyadaki kopukluk, kopyanın kendisinin
   hasarlı olduğu anlamına gelir (örneğin QueryProxy yazarken alınmış düz bir
   dosya kopyası); önceki yedeği deneyin. Bkz.
   [Denetim zinciri ve SIEM dışa aktarımı](/tr/docs/denetim-kaydi-ve-siem/).
5. Plan buysa yükseltmeyi ancak bundan sonra yapın.

Geri yüklenmiş bir kurulumda, chat düğmeleri saatler ya da günler önceki
mesajlara ait olan `pending` talepler olabilir. Böyle bir mesajın düğmelerinin
süresi dolmuş olabilir; hatırlatma, hâlâ pending olan bir talep için yeni bir
kart gönderir. Taleplerin kendisi asla süresi dolup düşmez.

## Sonraki adımlar

- [Yükseltme ve geri alma](/tr/docs/yukseltme/)
- [Sorun giderme](/tr/docs/sorun-giderme/)
- [Tehdit modeli](/tr/docs/tehdit-modeli/)
