Caddy ile Multi-App Hosting Yapılandırması

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-app dizininde
  • 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.

Yorum yapın