Kendi Sunucunda Üretim Altyapısı Kurmak — Coolify, Traefik ve VPS
Neden Self-hosted?
Aylık 200$ cloud faturası görünce insan düşünmeye başlıyor. Küçük-orta ölçekli projeler için managed Kubernetes, managed database ve managed her şey gerçekten gerekli mi?
Hesap yaptım:
- Hetzner CX31 (2 vCPU, 8GB RAM): €8.9/ay
- Hetzner CX41 (4 vCPU, 16GB RAM): €18.9/ay
Aynı spesifikasyonlarda AWS EC2 + RDS + Load Balancer: $150–200/ay.
Fark %90. Trade-off: yönetim sorumluluğu size ait.
Mimari
Internet
│
▼
Hetzner VPS (CX41)
│
├── Traefik (port 80/443, reverse proxy + SSL)
│ │
│ ├── app1.domain.com → Container A
│ ├── app2.domain.com → Container B
│ └── coolify.domain.com → Coolify UI
│
├── Coolify (PaaS katmanı)
│ ├── App deploy yönetimi
│ ├── Database yönetimi
│ └── Cron & worker yönetimi
│
└── Docker Engine
Adım 1: VPS Güvenliği
Sunucu ayağa kalktığında ilk iş güvenlik:
# Root ile giriş — son kez
ssh root@YOUR_IP
# Yeni kullanıcı
adduser deploy
usermod -aG sudo deploy
# SSH key kopyala
mkdir /home/deploy/.ssh
cp ~/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
# Root SSH'ı kapat
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd
# Firewall
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
Adım 2: Docker & Coolify Kurulumu
# Docker
curl -fsSL https://get.docker.com | bash
usermod -aG docker deploy
# Coolify — tek komut kurulum
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
Coolify kurulduktan sonra http://YOUR_IP:8000 adresine gidin ve ilk admin hesabını oluşturun.
Adım 3: Traefik Yapılandırması
Coolify kendi Traefik instance'ını yönetiyor, ama custom konfigürasyon gerektiğinde:
# /data/coolify/proxy/docker-compose.yml (Coolify'ın yönettiği)
# Coolify UI'dan "Proxy Settings" ile özelleştirin
# Ekstra middleware için dynamic config
# /data/coolify/proxy/dynamic/security-headers.yml
http:
middlewares:
security-headers:
headers:
stsSeconds: 31536000
stsIncludeSubdomains: true
contentTypeNosniff: true
browserXssFilter: true
referrerPolicy: "strict-origin-when-cross-origin"
Adım 4: GitHub Actions ile Otomatik Deploy
Coolify webhook'ları destekliyor. Ama daha kontrollü bir flow için:
# .github/workflows/deploy.yml
name: Deploy to VPS
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build & push image
run: |
echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Deploy via Coolify webhook
run: |
curl -X GET \
"${{ secrets.COOLIFY_WEBHOOK_URL }}" \
-H "Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}"
- name: Health check
run: |
sleep 30
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://app.bariscanatakli.com/health)
if [ "$STATUS" != "200" ]; then
echo "Health check failed: $STATUS"
exit 1
fi
Adım 5: Monitoring
Hafif bir monitoring stack Coolify üzerinden deploy ediyorum:
# Uptime Kuma — self-hosted uptime monitor
# Coolify'da yeni servis → Docker image → louislam/uptime-kuma:latest
# Port: 3001, Volume: /app/data
# Grafana + Prometheus için Coolify'da "One Click Deploy" seçeneği mevcut
VPS metrikleri için Netdata kuruyorum — kurulumu 1 komut:
bash <(curl -Ss https://my-netdata.io/kickstart.sh)
Yaşanan Sorunlar ve Çözümleri
Problem: Traefik certificate çözümleme hatası
Let's Encrypt rate limit'e takılmak çok yaygın. Geliştirme aşamasında staging CA kullanın:
# Coolify → Proxy Settings → Let's Encrypt
caServer: https://acme-staging-v02.api.letsencrypt.org/directory
# Production'a geçince kaldır
Problem: Container OOM kill
Coolify'da her servis için memory limit tanımlayın:
- Coolify UI → Service → Advanced → Memory Limit
Problem: Disk dolması
Docker image ve log temizleme cron'u:
# /etc/cron.weekly/docker-cleanup
#!/bin/bash
docker system prune -af --filter "until=168h"
journalctl --vacuum-time=7d
Maliyet Özeti
| Kalem | Aylık |
|---|---|
| Hetzner CX41 | €18.9 |
| Hetzner Volume (50GB) | €2.5 |
| Domain | ~€1 |
| Toplam | ~€22 |
Önceki AWS stack: $180/ay. %88 tasarruf.
Ne Zaman Self-hosted Değil?
- SLA garantisi gereken enterprise müşterileriniz varsa
- 7/24 on-call DevOps ekibiniz yoksa ve sistem kritikse
- Compliance (SOC2, HIPAA) gereksinimleri varsa
Kişisel projeler ve küçük ürünler için self-hosted mükemmel. Büyüdükçe managed'a geçiş planını kafanızda tutun.