Tek bir sunucuda birden fazla web uygulaması çalıştırmak, hemen her sysadmin’in günlük rutininin parçası haline geldi. Nginx veya Apache ile bu işi yıllardır yapıyoruz, ancak Caddy’nin getirdiği sadelik ve otomatik HTTPS desteği, özellikle çok sayıda uygulamayı yönetmek zorunda olanlar için ciddi bir oyun değiştirici. Bu yazıda, gerçek dünya senaryolarına dayanan bir multi-app hosting yapılandırmasını Caddy üzerinde adım adım kuracağız.
Neden Caddy ile Multi-App Hosting?
Klasik reverse proxy kurulumlarında her yeni uygulama eklediğinizde SSL sertifikası almanız, virtual host dosyası yazmanız ve servisi yeniden başlatmanız gerekir. Caddy bu süreci inanılmaz derecede basitleştirir.
Caddy’nin multi-app hosting için öne çıkardığı avantajlar:
- Otomatik Let’s Encrypt ve ZeroSSL entegrasyonu, sertifika yönetimini unutturur
- Caddyfile sözdizimi son derece okunabilir ve bakımı kolaydır
- Reload işlemleri sıfır kesinti süresiyle gerçekleşir
- HTTP/2 ve HTTP/3 (QUIC) desteği kutunun dışından gelir
- Dahili Prometheus metrikleri ve loglama sistemi vardır
Senaryomuzda şu uygulamaları tek sunucuda barındıracağız: bir Node.js API servisi, bir Python/Django uygulaması, bir statik React sitesi ve bir Portainer yönetim paneli. Gerçek hayatta tam olarak böyle bir yapıyla karşılaşma ihtimaliniz oldukça yüksek.
Caddy Kurulumu
Sunucunuzun Ubuntu 22.04 veya Debian 12 çalıştırdığını varsayarak başlayalım. Caddy’yi resmi depolardan kurmak en sağlıklı yoldur.
# Gerekli araçları yükle
sudo apt update && sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
# Caddy GPG anahtarını ekle
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key'
| sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
# Caddy deposunu sisteme ekle
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt'
| sudo tee /etc/apt/sources.list.d/caddy-stable.list
# Caddy'yi kur
sudo apt update && sudo apt install caddy
# Servis durumunu kontrol et
sudo systemctl status caddy
Kurulum tamamlandıktan sonra Caddy, /etc/caddy/Caddyfile dosyasını yapılandırma dosyası olarak kullanır. Varsayılan yapılandırma dosyasını yedekleyip kendi yapılandırmamızı oluşturalım.
sudo cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.backup
sudo nano /etc/caddy/Caddyfile
Temel Caddyfile Yapısı
Caddyfile’ın sözdizimini anlamadan çok uygulama yönetmek zorlaşır. Temel yapıyı inceleyelim:
# Global ayarlar blogu
{
email [email protected]
admin localhost:2019
}
# Site blokları
example.com {
# Bu domain için yapılandırma
}
api.example.com {
# Bu subdomain için yapılandırma
}
Global blok isteğe bağlıdır ama multi-app senaryolarında mutlaka kullanmalısınız. Burada Let’s Encrypt için e-posta adresinizi, admin API’nin dinleyeceği adresi ve log seviyelerini tanımlarsınız.
Multi-App Yapılandırması: Adım Adım
Şimdi dört farklı uygulamayı yapılandıracağız. Uygulamalarımız şu portlarda çalışıyor:
- Node.js API:
localhost:3000 - Django uygulaması:
localhost:8000 - React statik site:
/var/www/react-appdizininde - Portainer:
localhost:9000
Tam Caddyfile Yapılandırması
{
email [email protected]
admin localhost:2019
# Otomatik HTTPS için staging ortamı (test için açabilirsiniz)
# acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
log {
level INFO
}
}
# Node.js API Servisi
api.sirketiniz.com {
reverse_proxy localhost:3000 {
# Upstream sağlık kontrolü
health_uri /health
health_interval 30s
health_timeout 5s
# Başlık düzenlemeleri
header_up Host {upstream_hostport}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
# CORS başlıkları
header {
Access-Control-Allow-Origin "https://sirketiniz.com"
Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Access-Control-Allow-Headers "Content-Type, Authorization"
}
log {
output file /var/log/caddy/api.log {
roll_size 50mb
roll_keep 5
}
}
}
# Django Uygulaması
app.sirketiniz.com {
reverse_proxy localhost:8000 {
header_up Host {upstream_hostport}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
# Django statik dosyaları doğrudan sun
handle_path /static/* {
root * /var/www/django-app/staticfiles
file_server
}
handle_path /media/* {
root * /var/www/django-app/media
file_server
}
log {
output file /var/log/caddy/django.log {
roll_size 50mb
roll_keep 5
}
}
}
# React Statik Site
sirketiniz.com {
root * /var/www/react-app/build
file_server
# React Router için SPA yapılandırması
try_files {path} /index.html
# Önbellek başlıkları
header /static/* {
Cache-Control "public, max-age=31536000, immutable"
}
header *.html {
Cache-Control "no-cache, no-store, must-revalidate"
}
log {
output file /var/log/caddy/react.log {
roll_size 50mb
roll_keep 5
}
}
}
# www yönlendirmesi
www.sirketiniz.com {
redir https://sirketiniz.com{uri} permanent
}
# Portainer (IP kısıtlamalı)
portainer.sirketiniz.com {
reverse_proxy localhost:9000
# Sadece belirli IP'lerden erişime izin ver
@blocked not remote_ip 192.168.1.0/24 203.0.113.10
respond @blocked "Erisim reddedildi" 403
log {
output file /var/log/caddy/portainer.log
}
}
Bu yapılandırmayı kaydedin ve Caddy’yi yeniden yükleyin:
# Yapılandırmayı test et (syntax kontrolü)
caddy validate --config /etc/caddy/Caddyfile
# Sıfır kesinti ile yeniden yükle
sudo systemctl reload caddy
Log Dizinini Hazırlama
Yapılandırmada /var/log/caddy/ dizinini kullandık, bu dizinin var olması ve Caddy’nin yazma iznine sahip olması gerekir.
sudo mkdir -p /var/log/caddy
sudo chown caddy:caddy /var/log/caddy
sudo chmod 750 /var/log/caddy
Path-Based Routing: Alt Domain Yerine Alt Yol Kullanmak
Bazı durumlarda farklı alt domainler kullanmak yerine tek domain üzerinden path-based routing yapmak istersiniz. Örneğin sirketiniz.com/api, sirketiniz.com/app gibi. Caddy bunu handle_path direktifi ile çok temiz bir şekilde çözer.
sirketiniz.com {
# Ana site - React uygulaması
handle / {
root * /var/www/react-app/build
file_server
try_files {path} /index.html
}
# /api/* yolları Node.js'e
handle_path /api/* {
reverse_proxy localhost:3000
}
# /admin/* yolları Django'ya
handle_path /admin/* {
reverse_proxy localhost:8000
}
# /uploads/* yolları dosya sunucusuna
handle_path /uploads/* {
root * /var/www/uploads
file_server browse
}
# Eşleşmeyen tüm istekler 404
handle {
respond "Sayfa bulunamadi" 404
}
}
Önemli not: handle ve handle_path arasındaki fark kritiktir. handle_path prefix’i path’den silerek upstream’e gönderir. Yani /api/users isteği Node.js’e /users olarak ulaşır. handle ise path’i olduğu gibi bırakır.
Docker ile Entegrasyon
Modern sysadmin ortamlarında uygulamaların büyük çoğunluğu Docker container’larında çalışır. Caddy’yi Docker Compose ile birlikte kullanmak için iki yaygın yöntem vardır.
Yöntem 1: Host Network ile Caddy
# docker-compose.yml
version: '3.8'
services:
nodejs-api:
image: node:18-alpine
container_name: nodejs-api
ports:
- "3000:3000"
volumes:
- ./api:/app
working_dir: /app
command: node server.js
restart: unless-stopped
django-app:
image: python:3.11-slim
container_name: django-app
ports:
- "8000:8000"
volumes:
- ./django:/app
working_dir: /app
command: gunicorn myapp.wsgi:application --bind 0.0.0.0:8000
restart: unless-stopped
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
ports:
- "9000:9000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
restart: unless-stopped
volumes:
portainer_data:
Yöntem 2: Caddy’yi Container Olarak Çalıştırmak
Tüm stack’i Docker ile yönetmek istiyorsanız Caddy’yi de container olarak çalıştırabilirsiniz:
version: '3.8'
services:
caddy:
image: caddy:2-alpine
container_name: caddy
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3 için
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
- /var/www:/var/www:ro
restart: unless-stopped
networks:
- app_network
nodejs-api:
image: node:18-alpine
container_name: nodejs-api
expose:
- "3000"
networks:
- app_network
restart: unless-stopped
django-app:
image: python:3.11-slim
container_name: django-app
expose:
- "8000"
networks:
- app_network
restart: unless-stopped
volumes:
caddy_data:
caddy_config:
networks:
app_network:
driver: bridge
Bu durumda Caddyfile’da localhost yerine container isimlerini kullanın:
api.sirketiniz.com {
reverse_proxy nodejs-api:3000
}
app.sirketiniz.com {
reverse_proxy django-app:8000
}
Güvenlik Yapılandırması
Multi-app ortamlarda güvenlik katmanları eklemek kritik önem taşır. Caddy’nin built-in özellikleri bu konuda oldukça yardımcıdır.
Basic Authentication Ekleme
Belirli uygulamalara erişimi şifreyle korumak için:
# Şifrelenmiş parola oluştur
caddy hash-password --plaintext "GucluParolaniz123"
# Çıktı: $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG
monitoring.sirketiniz.com {
reverse_proxy localhost:9090
basicauth /* {
admin $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG
}
}
Rate Limiting ve Güvenlik Başlıkları
api.sirketiniz.com {
reverse_proxy localhost:3000
# Güvenlik başlıkları
header {
# Clickjacking koruması
X-Frame-Options "SAMEORIGIN"
# XSS koruması
X-Content-Type-Options "nosniff"
X-XSS-Protection "1; mode=block"
# HSTS
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Referrer politikası
Referrer-Policy "strict-origin-when-cross-origin"
# Hassas başlıkları kaldır
-Server
-X-Powered-By
}
}
Load Balancing ile Yüksek Erişilebilirlik
Aynı uygulamanın birden fazla instance’ı varsa Caddy’nin dahili load balancer’ını kullanabilirsiniz:
api.sirketiniz.com {
reverse_proxy {
# Üç farklı instance
to localhost:3000 localhost:3001 localhost:3002
# Load balancing yöntemi
lb_policy round_robin
# Alternatif: least_conn, ip_hash, random, first
# Sağlık kontrolü
health_uri /health
health_interval 10s
health_timeout 3s
health_status 200
# Başarısız instance için yeniden deneme
max_fails 3
fail_duration 30s
}
}
Load balancing politikaları:
- round_robin: İstekleri sırayla dağıtır, en yaygın kullanılan yöntemdir
- least_conn: En az bağlantısı olan sunucuya yönlendirir
- ip_hash: Aynı IP her zaman aynı sunucuya gider, session tutarlılığı için kullanılır
- random: Rastgele seçim yapar
- first: Sağlıklı ilk sunucuya yönlendirir
Yapılandırma Doğrulama ve Sorun Giderme
Production ortamında değişiklik yapmadan önce her zaman yapılandırmayı test edin:
# Sözdizimi kontrolü
caddy validate --config /etc/caddy/Caddyfile
# Yapılandırmayı formatlı göster (hataları kolayca görürsünüz)
caddy fmt /etc/caddy/Caddyfile
# Yerinde formatla ve kaydet
caddy fmt --overwrite /etc/caddy/Caddyfile
# Canlı yapılandırmayı JSON olarak göster
curl -s localhost:2019/config/ | python3 -m json.tool
# Caddy loglarını izle
sudo journalctl -u caddy -f
# Belirli bir domain için sertifika durumunu kontrol et
curl -s localhost:2019/pki/ca/local | python3 -m json.tool
Sık Karşılaşılan Sorunlar
Port 80/443 zaten kullanımda hatası:
# Hangi process port kullanıyor?
sudo ss -tlnp | grep -E ':80|:443'
sudo fuser 80/tcp 443/tcp
# Nginx veya Apache çalışıyorsa durdur
sudo systemctl stop nginx
sudo systemctl disable nginx
SSL sertifikası alınamıyor:
# DNS çözümlemesini test et
dig +short api.sirketiniz.com
# Let's Encrypt bağlantısını test et
curl -v https://acme-v02.api.letsencrypt.org/directory
# Caddy'nin sertifika depolama dizinini kontrol et
ls -la /var/lib/caddy/.local/share/caddy/certificates/
Reverse proxy 502 Bad Gateway hatası:
# Upstream uygulamanın çalıştığını doğrula
curl -v http://localhost:3000/health
# Caddy'nin upstream'e erişebildiğini test et
sudo -u caddy curl -v http://localhost:3000/health
Ortam Bazlı Yapılandırma
Geliştirme, test ve production ortamları için farklı yapılandırmalar yönetmek gerekebilir. Caddy’nin environment variable desteğini kullanın:
# /etc/caddy/Caddyfile
{
email {$CADDY_EMAIL}
}
{$APP_DOMAIN} {
reverse_proxy localhost:{$APP_PORT}
}
# Caddy servis dosyasına environment variable ekle
sudo systemctl edit caddy
[Service]
Environment="[email protected]"
Environment="APP_DOMAIN=app.sirketiniz.com"
Environment="APP_PORT=8000"
sudo systemctl daemon-reload
sudo systemctl restart caddy
Performans İzleme
Caddy’nin admin API’si üzerinden anlık metrikler alabilirsiniz:
# Aktif bağlantıları ve istatistikleri göster
curl -s localhost:2019/metrics
# Tüm yapılandırılmış route'ları listele
curl -s localhost:2019/config/apps/http/servers/ | python3 -m json.tool
# Belirli bir sitenin yapılandırmasını sorgula
curl -s "localhost:2019/config/apps/http/servers/srv0/routes" | python3 -m json.tool
Prometheus ile entegrasyon için /metrics endpoint’ini etkinleştirin:
{
servers {
metrics
}
}
Sonuç
Caddy ile multi-app hosting yapılandırması, geleneksel web sunucularına kıyasla ciddi ölçüde daha az zaman ve bakım gerektiriyor. Tek bir Caddyfile içinde onlarca uygulamayı yönetmek, her biri için ayrı ayrı SSL sertifikası takip etmek zorunda kalmamak ve sıfır kesinti süreli reload imkanı, bu aracı modern sysadmin ortamlarında çok değerli kılıyor.
Bu yazıda ele aldığımız yapılandırmaları gerçek dünyada kullanırken şu noktalara dikkat edin: DNS kayıtlarınızın sunucu IP’sine doğru işaret ettiğinden emin olun, Let’s Encrypt rate limit’lerine takılmamak için önce staging ortamında test yapın ve yapılandırma değişikliklerini her zaman caddy validate ile doğrulayın.
Caddy’nin Caddyfile syntax’ı başlangıçta basit görünse de production ortamlarında load balancing, güvenlik katmanları ve izleme gereksinimlerini bir arada yönetmek istediğinizde gerçek gücünü ortaya koyuyor. Nginx veya Apache’den geçiş yapmayı düşünüyorsanız, küçük bir projede denemekle başlamanızı öneririm; alışma süreci tahmin ettiğinizden çok daha kısa olacaktır.