Pushizo — Yayına alma (adım adım)
Bu belge, tek sunucuda (Docker Compose) ilk canlı kurulum içindir. Ayrıntılı kontrol listesi GO_LIVE_CHECKLIST.md, altyapı ayrıntıları infra/README.md. Kubernetes için docs/runbooks/helm-production.md.
Bu belge, tek sunucuda (Docker Compose) ilk canlı kurulum içindir. Ayrıntılı kontrol listesi GO_LIVE_CHECKLIST.md,
altyapı ayrıntıları infra/README.md. Kubernetes için docs/runbooks/helm-production.md.
0. Hazırlanacaklar (ürün sahibi)
| # | Ne | Nereden | Not |
|---|---|---|---|
| 1 | Alan adı | Herhangi bir kayıt firması | Ör. pushizo.com |
| 2 | Sunucu | Hetzner / DigitalOcean vb. | Ubuntu 24.04, başlangıç için 4 vCPU / 8 GB RAM / 80 GB SSD (plan 08) |
| 3 | DNS A kayıtları | Alan adı paneli | api, app, status, docs, grafana, storage alt alan adları + kök/www (tanıtım sitesi) → sunucu IP'si |
| 4 | Sistem e-postası | SMTP / Amazon SES / Resend | Kayıt doğrulama ve parola sıfırlama e-postaları için zorunlu; SPF/DKIM/DMARC kayıtları |
| 5 | Apple push | Apple Developer → Keys | .p8 anahtarı, Key ID, Team ID, Bundle ID (panelde app kurulumunda girilir) |
| 6 | Android push | Firebase konsolu | Servis hesabı JSON'u (panelde app kurulumunda yüklenir) |
| 7 | Yasal metinler | Hukuk | Gizlilik, Kullanım Şartları, KVKK Aydınlatma, Çerez — apps/web/content/legal/ (tanıtım sitesi) |
| 8 | (İsteğe bağlı) Ödeme | Stripe veya iyzico | Yoksa ücretli plana geçiş kapalıdır; platform ücretsiz planla ve admin panelinden atanan planlarla çalışır |
Push ve e-posta kimlik bilgileri sunucuya değil, panelden app başına girilir (şifreli saklanır).
Pushizo için durum (2026-10-16)
- Alan adı
pushizo.com(DNS Cloudflare'de). Sistem e-postasıbildirim@pushizo.com, SMTPmta1.mail.tecrube.com:465(SSL). - Depo kökünde (git dışı,
.gitignore).env.prodhazırlandı: pushizo.com adresleri, SMTP ve rastgele üretilmiş gizli değerler. Sunucuya kopyalanır (chmod 600); Stripe/iyzico yoksaBILLING_PROVIDER=autoile ücretli plana geçiş kapalı kalır. - GitHub değişkenleri:
PROD_APP_URL=https://app.pushizo.com,PROD_API_URL=https://api.pushizo.com,PROD_SITE_URL=https://pushizo.com,PROD_DOCS_URL=https://docs.pushizo.com.
E-posta teslim edilebilirliği (spam'e düşmeme)
Mevcut DNS'te SPF (-all), DKIM (default._domainkey, 2048 bit), DMARC (p=none) ve sunucu PTR'si (mta1.mail.tecrube.com)
doğru. Spam'in ana nedeni alan adının yeni olmasıdır (kayıt: 2026-10-16); Gmail/Outlook yeni alan adına itibar oluşana kadar
(genellikle 2–4 hafta) temkinli davranır. Yapılacaklar:
- Gmail'de gelen iletide Orijinali göster → SPF, DKIM, DMARC üçü de
PASSolmalı. DKIMPASSdeğilse barındırma firmasındanpushizo.comiçin DKIM imzalamanın açılmasını isteyin. - DMARC ve TLS-RPT raporları kendi alan adına alınır (2026-10-16 Cloudflare'de ayarlandı):
_dmarc→v=DMARC1; p=none; rua=mailto:dmarc@pushizo.com; fo=1,_smtp._tls→v=TLSRPTv1; rua=mailto:dmarc@pushizo.com.dmarc@pushizo.comkutusu açık olmalıdır. Raporlar Pushizo admin panelinde E-posta güvenliği sayfasında izlenir (aşağıda "DMARC raporları"); SPF/DKIM sürekli geçince panelp=quarantine'i önerir. - Google Postmaster Tools'a
pushizo.comekleyip doğrulayın (alan adı itibarı ve spam oranı buradan izlenir). - İlk haftalarda düşük hacimle, gerçek alıcılara gönderin; gelen spam kutusundaki iletileri Spam değil olarak işaretleyin.
- Bülten (
haber@pushizo.com) için ayrı alt alan adı önerilir (ör.haber.pushizo.com); toplu gönderim itibarı sistem e-postalarını (doğrulama, parola sıfırlama) etkilemesin.
DMARC raporları ve politika (ADR-0035)
DMARC toplu raporları ve TLS-RPT dmarc@pushizo.com adresine gelir. Pushizo bunları saklar, gönderen kaynakları yetkili /
yetkisiz olarak sınıflar, yeni yetkisiz kaynakta veya doğrulama oranı düştüğünde platform adminlerine e-posta gönderir
(kaynak/uyarı başına günde bir) ve 14 gün + %98 doğrulama sonrasında politikayı bir adım sıkılaştırmayı önerir
(none → quarantine → reject). Panel: https://app.pushizo.com/admin/email-security.
Raporları almak için iki yoldan birini (veya ikisini) kurun:
A) Postal HTTP Endpoint (önerilen; pushizo.com MX'i Postal'dadır):
- Belirteç üretin:
openssl rand -hex 24(48 karakter)..env.prod→DMARC_INBOUND_TOKEN=<belirteç>; API'yi yeniden başlatın (docker compose --env-file .env.prod -f infra/compose/docker-compose.prod.yml up -d api worker). - Postal paneli (
panel.mail.tecrube.com) →pushizo.comsunucusu → Routing → HTTP Endpoints → Add: URLhttps://api.pushizo.com/hooks/dmarc/<belirteç>, Encoding: Body as JSON, Format: Raw message, Strip replies kapalı. - Routing → Routes → Add route: Name
dmarc, Domainpushizo.com, Endpoint = 2. adımdaki HTTP endpoint, Mode Endpoint. Böylecedmarc@pushizo.comiçin ayrı posta kutusu gerekmez. - Postal'ın gelen posta spam denetimini açık tutun (sunucu ayarı Spam checking: SpamAssassin, eşik ~5). Rapor olmayan iletiler (spam) Pushizo tarafından 200 ile kabul edilip atılır; Postal yeniden denemez. Gövde sınırı 25 MB, hız sınırı IP başına dakikada 120 istek; yanlış belirteç 404.
- Doğrulama:
curl -s -o /dev/null -w '%{http_code}' -X POST https://api.pushizo.com/hooks/dmarc/yanlis -H 'content-type: application/json' -d '{}'→404. İlk raporlar 24–48 saat içinde gelir (Google, Microsoft, Yahoo).
B) IMAP posta kutusu (Postal route kurulamazsa):
- Barındırma panelinde
dmarc@pushizo.composta kutusunu oluşturun (güçlü parola). .env.prod→DMARC_IMAP_URL=imaps://dmarc%40pushizo.com:<URL kodlu parola>@<imap-sunucusu>:993(parolada@ : / ? # %varsa URL kodlayın:node -e "console.log(encodeURIComponent(process.argv[1]))" '<parola>'). Worker'ı yeniden başlatın.- Worker kutuyu 15 dakikada bir tarar (Redis kilidiyle tek çalıştırma): raporlar okunup
Processedklasörüne (yoksa oluşturulur), rapor olmayan iletiler çöp kutusuna taşınır. Panelde Şimdi tara ile hemen tetiklenebilir.
Cloudflare ile tek tıkla politika (isteğe bağlı): Cloudflare → My Profile → API Tokens → Create Token → "Edit zone DNS"
şablonu, Zone Resources = yalnızca pushizo.com. .env.prod → CLOUDFLARE_API_TOKEN=<belirteç>, CLOUDFLARE_ZONE_ID=\<pushizo.com Overview sayfasındaki Zone ID>; API'yi yeniden başlatın. Panelde Cloudflare ile uygula yalnızca p= etiketini değiştirir
(rua, fo korunur), işlem platform denetim kaydına yazılır ve Geri al ile önceki değere dönülür. Cloudflare
yapılandırılmazsa panel girilecek tam TXT değerini gösterir; kaydı Cloudflare'de elle güncelleyin.
1. GitHub ayarları (bir kez)
Settings → Secrets and variables → Actions (depo özel: github.com/exec0de/pushizo):
- Secrets:
PROD_SSH_HOST(sunucu IP'si veya adı),PROD_SSH_USER(dağıtım kullanıcısı, ör.deploy),PROD_SSH_KEY(bu kullanıcının özel SSH anahtarı; genel anahtarı sunucuda~/.ssh/authorized_keys). - Variables:
PROD_APP_URL=https://app.pushizo.com,PROD_API_URL=https://api.pushizo.com,PROD_SITE_URL=https://pushizo.com,PROD_DOCS_URL=https://docs.pushizo.com,DEPLOY_PATH=/opt/pushizo. İsteğe bağlı:PROD_INDEXNOW_KEY,PROD_CONTACT_EMAIL(sitede iletişim adresi, ör.bildirim@pushizo.com). - Staging: ayrı staging sunucusu yoksa hiçbir şey tanımlamayın;
STAGING_ENABLEDtanımsızken staging işi atlanır ve üretim dağıtımı doğrudan onaya düşer. Staging kullanılacaksaSTAGING_ENABLED=true+STAGING_SSH_HOST/USER/KEYsecret'ları +STAGING_APP_URL,STAGING_API_URLdeğişkenleri. Settings → Environments → production: zorunlu onaylayıcı (prod dağıtımı elle onaylanır).
2. Sunucu hazırlığı (bir kez)
# Sunucuda (root veya sudo):
apt-get update && apt-get install -y docker.io docker-compose-v2 git ufw
# Güvenlik duvarı (yalnızca Plesk OLMAYAN sunucu): SSH, HTTP, HTTPS. Plesk'li sunucuda ufw kullanmayın: §9.1.
ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp && ufw --force enable
# 8 GB RAM altı sunucularda takas alanı (Postgres/ClickHouse olmayan tek sunucu için 4 GB yeterli)
fallocate -l 4G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
# Dağıtım kullanıcısı (GitHub Actions bu kullanıcıyla SSH yapar)
adduser --disabled-password --gecos "" deploy && usermod -aG docker deploy
install -d -o deploy -g deploy -m 700 /home/deploy/.ssh
# PROD_SSH_KEY'in genel anahtarını /home/deploy/.ssh/authorized_keys dosyasına ekleyin (izin 600, sahibi deploy).
install -d -o deploy -g deploy /opt/pushizo
```text
Depo özel olduğu için klonlama **deploy key** ile, imajlar **GHCR girişi** ile alınır (ikisi de `deploy` kullanıcısıyla):
```bash
sudo -iu deploy
# 1) Salt okunur deploy key: genel anahtarı GitHub → depo → Settings → Deploy keys'e ekleyin ("Allow write access" kapalı).
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519 -C "pushizo-deploy" && cat ~/.ssh/id_ed25519.pub
ssh-keyscan github.com >> ~/.ssh/known_hosts
git clone git@github.com:exec0de/pushizo.git /opt/pushizo && cd /opt/pushizo
# 2) GHCR: yalnızca `read:packages` kapsamlı bir GitHub PAT (classic) ile; parola stdin'den okunur, geçmişe yazılmaz.
docker login ghcr.io -u <GITHUB_KULLANICI_ADINIZ> --password-stdin
# 3) Ortam dosyası
cp infra/compose/.env.prod.example .env.prod && chmod 600 .env.prod
```text
`.env.prod` içinde doldurulacaklar: `SMTP_URL` (parola URL kodlu), gizli değerler (`MASTER_KEY` 32 bayt base64,
`SESSION_SECRET` en az 32 karakter, `POSTGRES_*`, `REDIS_PASSWORD`, `STORAGE_ROOT_*`, `S3_ACCESS_KEY`/`S3_SECRET_KEY`,
`GRAFANA_ADMIN_PASSWORD`) ve yedekleme için ayrı `BACKUP_S3_ACCESS_KEY`/`BACKUP_S3_SECRET_KEY`. Rastgele değer:
`openssl rand -hex 32` (yalnızca `0-9a-f`). `POSTGRES_PASSWORD` ve `REDIS_PASSWORD` `DATABASE_URL`/`REDIS_URL` adreslerine
gömülür: base64 çıktısındaki `/ + =` (ya da `@ : ? # %`) adresi bozar ve `deploy.sh` bu durumda dağıtımı açık bir mesajla
durdurur (değer yazdırılmaz). `MASTER_KEY` istisnadır: 32 bayt base64 olmalıdır (örnek dosyadaki komut). Alan adları örnekte pushizo.com olarak hazırdır. `PROVIDER_MODE=real` ve
`ALLOW_MOCK_PROVIDERS=false` kalmalıdır; `ADMIN_MFA_REQUIRED` varsayılan `true`'dur (kapatmak bilinçli karar;
`GO_LIVE_CHECKLIST.md`). Üretimde kısa `SESSION_SECRET`, geliştirme S3 parolası ve `localhost` SMTP adresi reddedilir
(uygulama açılmaz).
Alarm e-postası (Alertmanager) için SMTP parolası ayrı bir gizli dosyaya yazılır (deploy betikleri dosya yoksa boş
oluşturur; boşken alarm e-postası gitmez). Slack isteğe bağlıdır: `secrets/slack_webhook_url` doluysa Slack de etkinleşir.
```bash
install -d -m 700 infra/compose/secrets
read -rs -p "SMTP parolası: " P && printf '%s' "$P" > infra/compose/secrets/smtp_password; unset P; echo
```text
Dizin 700'dür; dosya izni betiklerce 644 yapılır (Alertmanager konteyneri `nobody` kullanıcısıyla okur).
**Kurulu sistemde URL güvenli olmayan parolayı değiştirme:** Postgres rol parolası yalnızca ilk kurulumda (boş veri
dizini) ayarlanır; `.env.prod`'u değiştirmek tek başına yetmez. Yeni değeri `openssl rand -hex 32` ile üretip önce
veritabanında, sonra dosyada güncelleyin (parola komut satırına/geçmişe yazılmaz):
```bash
cd /opt/pushizo
read -rs -p "Yeni pushizo parolası: " P; echo
printf "alter role pushizo password '%s';\n" "$P" | docker compose --env-file .env.prod -f infra/compose/docker-compose.prod.yml \
exec -T postgres psql -U postgres -d pushizo -v ON_ERROR_STOP=1 >/dev/null; unset P
# .env.prod → POSTGRES_PASSWORD=<aynı değer> (nano/vim ile), ardından: IMAGE_TAG=<sürüm> ./infra/scripts/deploy.sh prod
```text
`REDIS_PASSWORD` yalnızca `.env.prod`'da değiştirilir; `deploy.sh` Redis ve uygulamaları yeni değerle yeniden oluşturur.
## 3. DNS
Cloudflare'de (pushizo.com) aşağıdaki **A** kayıtlarını sunucu IP'sine yönlendirin; proxy durumu **DNS only (gri bulut)**
olmalıdır (Let's Encrypt HTTP doğrulaması ve nginx TLS'i doğrudan sunucuda çalışır):
| Ad | Tür | Değer |
|----|-----|-------|
| `pushizo.com` (`@`) | A | sunucu IP'si |
| `www` | A | sunucu IP'si (nginx kök alan adına 301 yönlendirir) |
| `app`, `api`, `docs`, `status`, `grafana`, `storage` | A | sunucu IP'si |
Mevcut MX/SPF/DKIM/DMARC kayıtlarına dokunmayın. Yayılmayı doğrulayın: `dig +short app.pushizo.com` sunucu IP'sini dönmeli.
## 4. Sürüm etiketi → imajlar
Geliştirici makinesinde (sürüm `package.json`, `apps/api/src/version.ts` ve `CHANGELOG.md` ile eşleşmeli):
```bash
git tag v1.0.0 && git push origin v1.0.0
```text
`release.yml` imajları GHCR'ye koyar (`ghcr.io/exec0de/pushizo-*:1.0.0`, etiket `v` önekisiz) ve GitHub Release oluşturur.
Üretim dağıtımı işi `production` onayında bekler: **5. adım bitmeden onaylamayın** (sertifikalar yokken nginx açılmaz).
## 5. TLS ve ilk dağıtım (sunucuda, `deploy` kullanıcısı)
```bash
cd /opt/pushizo && git fetch --tags && git checkout -q v1.0.0
IMAGE_TAG=1.0.0 ./infra/scripts/certbot-init.sh # tüm alan adları + www için Let's Encrypt sertifikası
IMAGE_TAG=1.0.0 ./infra/scripts/deploy.sh prod # migrate → api-1/api-2 → worker'lar → dashboard/docs/web → nginx
```text
Ardından GitHub Actions'ta üretim dağıtımını onaylayın: aynı sürümü yeniden uygular (idempotent) ve duman testini
(`/healthz` sürümü, status.json, dashboard) çalıştırır. Sonraki sürümlerde yalnızca etiket itilir ve onay verilir.
## 6. İlk giriş ve yönetici
1. `https://app.pushizo.com/register` ile hesap aç, e-postayı doğrula.
2. Hesabı platform yöneticisi yap (sunucuda, bir kez):
```bash
docker compose --env-file .env.prod -f infra/compose/docker-compose.prod.yml exec postgres \
psql -U postgres -d pushizo -c "update accounts set is_platform_admin = true where lower(email) = lower('SENIN@EPOSTAN');"
```text
3. `https://app.pushizo.com/admin` (MFA açıksa önce Hesabım → iki aşamalı doğrulama).
## 7. İlk app ve uygulamaları bağlama
Panelde **Yeni app** → platform seç → `.p8` / Firebase JSON / site adresi → doğrulama → verilen SDK kodunu uygulamana ekle →
ilk abone gelince **test bildirimi**. OneSignal'dan geçişte aboneler **Veri → İçe aktar → OneSignal CSV** ile aktarılır;
birkaç gün iki sistem paralel çalıştırılıp sonra OneSignal kapatılır.
## 8. Yayın sonrası
- `https://status.pushizo.com` ve Grafana panoları (`https://grafana.pushizo.com`); alarmlar `infra/monitoring` (e-posta: `bildirim@pushizo.com`).
- Yedek: `sudo bash infra/scripts/install-cron.sh` (bir kez; idempotent) → `/etc/cron.d/pushizo` her gün 03:30
`backup.sh` (PostgreSQL `pg_dump` + RustFS nesne depolama aynası → sunucu dışı kova; wal-g açıksa 03:00 `walg-backup.sh`),
günlük `/var/log/pushizo-backup.log` (logrotate haftalık, 8 kopya), tazelik metriği
`/var/lib/node_exporter/textfile/pushizo_backup_*.prom` → 26 saati aşınca `PushizoBackupStale` alarmı. AWS CLI ana
makineye kurulmaz (`amazon/aws-cli` sabit sürüm imajı, `AWS_CLI_IMAGE`). İlk yedeği hemen deneyin:
`sudo /opt/pushizo/infra/scripts/backup.sh` ve bir geri yükleme tatbikatı yapın (`docs/runbooks/restore.md`).
- Arama motorları: `docs/guides/seo.md` (Search Console, sitemap).
## 9. Plesk'li sunucu (EDGE_MODE=plesk)
80/443 ve TLS Plesk'in nginx'indedir; Pushizo nginx konteyneri yalnızca `127.0.0.1:EDGE_PORT` (18080) dinler
(`infra/compose/docker-compose.plesk.yml`). Alan adları ve sertifikalar `sudo ./infra/scripts/plesk-setup.sh` ile kurulur
(yükleme sınırı 64 MB, Pushizo nginx'iyle aynı). `LOCAL_BUILD=1` ise imajlar sunucuda derlenir; `deploy.sh` başarılı
dağıtımdan sonra bir haftadan eski kullanılmayan imajları ve 10 GB üzerindeki derleme önbelleğini temizler. GitHub'da
`LOCAL_BUILD=true` değişkeni tanımlanırsa dağıtım GHCR imajlarına bağlı kalmaz (`release.yml`).
### 9.1 Güvenlik duvarı (Plesk Firewall)
Plesk sunucusunda `ufw`/`iptables` kurallarını elle yazmayın: Plesk Firewall kurallarını ezer ya da onlarla çakışır ve
posta/panel erişimi kesilebilir. **Tools & Settings → Firewall** (Plesk Firewall eklentisi) üzerinden yönetin:
| Port | Hizmet | Durum |
|------|--------|-------|
| 22/tcp (ya da özel SSH portu, ör. 1981) | SSH | açık; mümkünse yalnızca yönetici IP'lerine |
| 80/tcp, 443/tcp | HTTP/HTTPS (Plesk nginx → Pushizo) | açık |
| 8443/tcp | Plesk paneli | açık; mümkünse yalnızca yönetici IP'lerine |
| 25, 465, 587/tcp | SMTP (gelen posta, gönderim) | açık kalmalı |
| 993, 143/tcp | IMAP (SSL / düz) | açık kalmalı |
| 995, 110/tcp | POP3 (SSL / düz) | açık kalmalı |
| 4190/tcp | ManageSieve (posta filtreleri) | açık kalmalı |
`EDGE_PORT` (18080) ve konteyner portları (Postgres, Redis, Grafana, Prometheus) dışarı açılmaz: compose bunları
yayınlamaz, nginx yalnızca `127.0.0.1`'e bağlanır. Docker kendi iptables zincirlerini yönetir; Plesk Firewall'da
"Docker" ile ilgili kuralları silmeyin. Değişikliği uygularken Plesk 60 sn içinde onay ister; erişim kesilirse kurallar
otomatik geri alınır.
### 9.2 SSH sertleştirme (elle; betikler SSH ayarını değiştirmez)
1. Önce anahtarla girişin çalıştığını **ayrı bir oturumda** doğrulayın (`ssh -i ~/.ssh/<anahtar> <kullanıcı>@<sunucu>`);
`deploy` kullanıcısının `~/.ssh/authorized_keys` dosyasında GitHub Actions genel anahtarı olmalıdır.
2. `/etc/ssh/sshd_config.d/99-pushizo.conf` dosyasına:
```text
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
PubkeyAuthentication yes
```text
Ardından `sshd -t && systemctl reload ssh`. Mevcut oturumu kapatmadan yeni bir oturumla girişi tekrar deneyin.
3. Özel SSH portu kullanılıyorsa (ör. 1981) Plesk Firewall'da açın, GitHub'daki `PROD_SSH_HOST` için `~/.ssh/config`
ya da iş akışında `-p` gerekir; port değişikliği yapılmadıysa 22 kalır.
4. **fail2ban:** Plesk → Tools & Settings → IP Address Banning (Fail2Ban) → **Enable intrusion detection**; `plesk-ssh`
(ya da `ssh`) jail'i açık olmalı. GitHub Actions çıkış IP'leri değişkendir: başarısız girişler engellenirse
jail'i kontrol edin (`fail2ban-client status sshd`), yönetici IP'nizi "Trusted IP Addresses" listesine ekleyin.
### 9.3 Yedek ve izleme
- `sudo bash infra/scripts/install-cron.sh` → `/etc/cron.d/pushizo` (03:30 yedek) ve `/etc/logrotate.d/pushizo`; Plesk'in
kendi "Scheduled Tasks" ekranında görünmez, `cat /etc/cron.d/pushizo` ile kontrol edilir.
- Prometheus iç nginx probu (`http://nginx/nginx-health`) Plesk modunda da çalışır (konteyner içi 80 dinlenir, dışarı
yayınlanmaz); dış problar `www`, `grafana`, `storage` dahil tüm alan adlarını ve sertifika bitişini (Plesk Let's
Encrypt) izler.Kendi sunucunuzda çalıştırma
Pushizo'yu kendi altyapınızda kurma seçenekleri — tek sunucu Docker Compose ve Kubernetes Helm; bileşenler, sistem gereksinimleri, ortam değişkenleri, imajlar, güncelleme ve yedekleme özeti.
Helm ile üretim dağıtımı (Kubernetes)
v2 Faz 16 madde 4 (docs/plan/10). Chart: infra/k8s/pushizo. Değer örnekleri: values-production.yaml, values-staging.yaml. Doğrulama: values.schema.json (tek alan) + templates/validate.yaml (alanlar arası kurallar,…