Altyapı Çalışması — Tailscale VPN ve Quartz Geçişi
İki fazlı büyük altyapı revizyonu: (1) VPS servislerini Tailscale VPN arkasına alma, (2) Ghost blog’u Quartz (Obsidian tabanlı SSG) ile değiştirme.
Faz 1: Tailscale Kurulumu
Amaç
VPS’teki servisleri (Coolify, n8n, SSH) public internet’ten gizleyip sadece Tailscale VPN üzerinden erişilebilir yapmak. Blog public kalacak.
Cihazlar ve IP’ler
| Cihaz | Tailscale IP | Not |
|---|---|---|
| PC | 100.111.5.43 | Windows 11 |
| Telefon | 100.119.64.118 | Android 16 |
| VPS (Hetzner) | 100.90.102.23 | Ubuntu 24.04 |
| VPS Public IP | 37.27.25.86 | Sadece blog için açık |
1.1 — PC ve Telefon
- tailscale.com’dan hesap (GitHub OAuth)
- PC: tailscale.com/download → Windows installer
- Telefon: Play Store → Tailscale app
- Ücretsiz plan, 3 cihaz için yeterli
1.2 — VPS Kurulumu
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
# Auth URL tarayıcıda onaylandı
tailscale ip -4 # 100.90.102.231.3 — Tailscale Üzerinden SSH Doğrulama
# PC'den (PowerShell):
ssh [email protected]1.4 — UFW ile Public SSH Kapatma
sudo ufw allow in on tailscale0
sudo ufw delete allow OpenSSHSonuç UFW tablosu:
80 ALLOW Anywhere # Blog
443 ALLOW Anywhere # Blog SSL
Anywhere on tailscale0 ALLOW Anywhere # Tailscale tam erişim
1.5 — Docker Portlarını Tailscale Arkasına Alma
Docker portları UFW’yi bypass ediyor. DOCKER-USER chain ile düzeltildi:
# Tailscale'den gelen her şeye izin
sudo iptables -I DOCKER-USER 1 -i tailscale0 -j RETURN
# Mevcut bağlantılar kopmasın
sudo iptables -I DOCKER-USER 2 -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN
# Blog için 80/443 public kalsın
sudo iptables -I DOCKER-USER 3 -i eth0 -p tcp -m multiport --dports 80,443 -j RETURN
# Geri kalan dış trafiği engelle
sudo iptables -I DOCKER-USER 4 -i eth0 -j DROP1.6 — iptables Kurallarını Kalıcı Yapma
Bu adımda UFW kaldırıldı, yerine
iptables-persistentgeldi. Aynı işi yapıyor.
sudo apt install iptables-persistent -y # İki kez "Yes"
sudo netfilter-persistent saveErişim Bilgileri (Tailscale Sonrası)
| Servis | Eski Erişim | Yeni Erişim |
|---|---|---|
| Coolify | panel.egebostanci.me | http://100.90.102.23:8000 |
| n8n | n8n.egebostanci.me | https://n8n.egebostanci.me (auth arkasında) |
| SSH | ssh [email protected] | ssh [email protected] |
| Blog | egebostanci.me | egebostanci.me (değişmedi) |
n8n.egebostanci.me ve panel.egebostanci.me hâlâ Coolify proxy üzerinden açılıyor (80/443 public). Auth koruması yeterli görüldü, domain’ler bırakıldı.
Faz 2: Ghost → Quartz Blog Geçişi
Amaç
Ghost blog’u kaldırıp yerine Quartz (Obsidian tabanlı static site generator) koymak. Obsidian’dan yazıp otomatik yayınlama.
2.1 — WordPress XML’den Markdown’a Dönüştürme
Eski blogumun WordPress XML export’undan başladım: 165 yazı, 9 kategori (2012’den 2025’e uzanan kişisel arşiv).
Dönüştürme Python script ile yapıldı. Her yazı:
- Yıl klasörüne ayrıldı (2012-2019, 2025)
- Frontmatter eklendi (title, date, publish, categories, tags)
- HTML → Markdown dönüştürüldü
- Aynı tarih+isimli yazılar için numara suffix’i eklendi
2.2 — Quartz Kurulumu (VPS)
cd /opt
git clone https://github.com/jackyzha0/quartz.git
cd quartz
npm i2.3 — Config Düzenlemesi
/opt/quartz/quartz.config.ts:
pageTitle: "Ege Bostancı"
pageTitleSuffix: ""
analytics: null
locale: "tr-TR"
baseUrl: "egebostanci.me"
defaultDateType: "created"
// CustomOgImages kaldırıldı (build hızı için)2.4 — XML’i VPS’e Atıp Çevirme
# PC'den (PowerShell):
scp "${env:USERPROFILE}\Desktop\izadam.WordPress.2025-08-22.xml" [email protected]:/opt/quartz/export.xml
# VPS'te Python script ile çevrildi
# Çıktı: /opt/quartz/content/arsiv/2012-2019/ altında 165 .md dosyası2.5 — Ana Sayfa
/opt/quartz/content/index.md:
---
title: Ege Bostancı
publish: true
---
Merhaba, ben Ege. Burası benim kişisel köşem.
## Arşiv (2012-2019)
Eski blogum **İşiz Adam**'dan taşınan yazılar. Lise yıllarından başlayıp uzanan, çoğu kişisel, çoğu ham.
→ [Arşive gözat](arsiv)2.6 — Build ve Test
cd /opt/quartz && npx quartz build
# 166 dosya işlendi, 74 draft filtrelendi, 114 sayfa yayınlandı2.7 — Nginx ile Serve Etme (Coolify Üzerinden)
Ghost durdurulup silindi. Coolify’da yeni Docker Compose resource oluşturuldu:
services:
quartz:
image: nginx:alpine
volumes:
- /opt/quartz-public:/usr/share/nginx/html:ro
- /opt/quartz-serve/nginx.conf:/etc/nginx/conf.d/default.conf:ro
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 30s
timeout: 5s
retries: 3Domain: https://egebostanci.me — Coolify servis settings’inden ayarlandı.
Nginx config (/opt/quartz-serve/nginx.conf):
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri.html $uri/ =404;
}
error_page 404 /404.html;
}Önemli:
quartz buildher seferindepublic/klasörünü silip yeniden oluşturur. Bu Docker bind mount’u kırıyor. Çözüm: ayrı/opt/quartz-publicklasörü + rsync.
mkdir -p /opt/quartz-public
rsync -a --delete /opt/quartz/public/ /opt/quartz-public/
chmod -R 755 /opt/quartz-public2.8 — Obsidian → Syncthing → VPS → Auto-Rebuild Pipeline
Syncthing zaten Obsidian vault’unu VPS’e sync ediyor:
- Host path:
/root/syncthing/data/obsidian/ - Container path:
/var/syncthing/Sync/obsidian/
Obsidian’da blog/ klasörü oluşturuldu. VPS’te symlink:
ln -s /root/syncthing/data/obsidian/blog /opt/quartz/content/blogAuto-rebuild script (/opt/quartz/auto-rebuild.sh):
#!/bin/bash
WATCH_DIR="/root/syncthing/data/obsidian/blog"
QUARTZ_DIR="/opt/quartz"
SERVE_DIR="/opt/quartz-public"
echo "Blog klasörü izleniyor: $WATCH_DIR"
inotifywait -m -r -e modify,create,delete,moved_to "$WATCH_DIR" |
while read -r directory events filename; do
if [[ "$filename" == *.md ]]; then
echo "$(date): Değişiklik - $filename ($events)"
sleep 3
cd "$QUARTZ_DIR" && npx quartz build 2>&1 | tail -3
rsync -a --delete "$QUARTZ_DIR/public/" "$SERVE_DIR/"
chmod -R 755 "$SERVE_DIR"
echo "$(date): Rebuild + sync tamamlandı"
fi
doneGerekli paket:
apt install inotify-tools -ySystemd service (/etc/systemd/system/quartz-rebuild.service):
[Unit]
Description=Quartz Auto Rebuild
After=network.target
[Service]
Type=simple
ExecStart=/opt/quartz/auto-rebuild.sh
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetBaşlatma:
systemctl daemon-reload
systemctl enable quartz-rebuild
systemctl start quartz-rebuildYeni Yazı Akışı
- Obsidian’da
blog/klasörüne.mddosyası oluştur - Frontmatter ekle:
--- title: "Yazı Başlığı" date: 2026-04-23 publish: true --- - Kaydet
- ~15 saniye bekle (Syncthing + rebuild + rsync)
- egebostanci.me güncellenir
Content Yapısı
/opt/quartz/content/
├── index.md ← Ana sayfa
├── arsiv/ ← WordPress'ten taşınan 165 yazı
│ ├── 2012/ (16 yazı)
│ ├── 2013/ (33 yazı)
│ ├── 2014/ (32 yazı)
│ ├── 2015/ (31 yazı)
│ ├── 2016/ (24 yazı)
│ ├── 2017/ (9 yazı)
│ ├── 2018/ (3 yazı)
│ ├── 2019/ (1 yazı)
│ └── 2025/ (16 yazı)
└── blog/ → symlink → /root/syncthing/data/obsidian/blog/
Sonrasında (24 Nisan)
Bir gün sonra n8n tarafında self-healing paketi tamamlandı: Idempotency Gate, Finalizer node, Global Error Handler workflow, Postgres ledger tablosu (processed_requests). Detaylar ayrı bir yazıda: n8n İzleme Mimarisi ve Hermes Pivotu.
Aynı gün ayrıca public SSH kapatıldı (kritik düzeltme — UFW → iptables-persistent geçişinde eski delete allow OpenSSH kuralı migrate olmamış, fark edildi). Şimdi 22 portu eth0’dan DROP’lu, sadece Tailscale üzerinden erişilebilir.
Dış Port Tarama Sonucu (37.27.25.86)
| Port | Durum | Amaç |
|---|---|---|
| 22 | Kapalı | SSH (Tailscale-only) |
| 80 | Açık | Blog HTTP |
| 443 | Açık | Blog HTTPS |
| 6001, 6002, 8000, 8080 | Kapalı | Coolify/Traefik iç portlar (DOCKER-USER bloklu) |
| 22000, 21027 | Kapalı | Syncthing (INPUT DROP) |
Önemli Dosya Yolları (VPS)
| Dosya/Klasör | Yol |
|---|---|
| Quartz repo | /opt/quartz/ |
| Quartz config | /opt/quartz/quartz.config.ts |
| Quartz content | /opt/quartz/content/ |
| Quartz public (build çıktısı) | /opt/quartz/public/ |
| Nginx serve klasörü | /opt/quartz-public/ |
| Nginx config | /opt/quartz-serve/nginx.conf |
| Auto-rebuild script | /opt/quartz/auto-rebuild.sh |
| Systemd service | /etc/systemd/system/quartz-rebuild.service |
| Obsidian vault (Syncthing) | /root/syncthing/data/obsidian/ |
| Blog symlink | /opt/quartz/content/blog → /root/syncthing/data/obsidian/blog/ |
| iptables kuralları | /etc/iptables/rules.v4 |
Faydalı Komutlar
# Quartz manual rebuild
cd /opt/quartz && npx quartz build && rsync -a --delete /opt/quartz/public/ /opt/quartz-public/ && chmod -R 755 /opt/quartz-public
# Auto-rebuild servis durumu
systemctl status quartz-rebuild
# Auto-rebuild logları
journalctl -u quartz-rebuild -f
# Tailscale durumu
tailscale status
# Docker container'ları
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# iptables kuralları
sudo iptables -L DOCKER-USER -n -vİlgili yazılar
- VDS’ten Hetzner’e Göç — Coolify, Docker ve Çözülen Sorunlar — bu altyapının önceki hali ve göç hikayesi
- arsiv.egebostanci.me — Windows’ta HFS + Cloudflare Tunnel — aynı domain altında çalışan ek bir self-host servis
- n8n İzleme Mimarisi ve Hermes Pivotu — sonrasında çıkan otomasyon kararları