Ghost + Caddy: Dedizierter Single-App-Server Setup (Production Ready)

Ghost + Caddy: Dedizierter Single-App-Server Setup (Production Ready)
  • ✅ Konfliktfrei
  • ✅ Wartungsarm
  • ✅ Schnell
  • ✅ Skalierbar (ohne Komplexität)

Hardware-Anforderungen (minimal)

  • CPU: 1-2 vCPU reicht vollkommen
  • RAM: 2-4GB (Ghost ~100MB, Caddy ~14MB, MySQL ~150-200MB)
  • Disk: 20-50GB je nach Traffic und Media
  • Bandbreite: Standard (bei Blog selten ein Problem)

Empfohlener VPS: Hetzner CX11, Netcup 512MB+, Contabo Basic

Architecture: Single Server, Caddy Full Power

┌─────────────────────────────────────────────────────────────┐
│                    INTERNET (Port 80/443)                   │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
        ┌────────────────────────┐
        │   CADDY REVERSE PROXY  │  ← Automatisches SSL/TLS
        │   (Port 80/443)        │  ← Certificate Management
        │   Single Caddyfile     │  ← Traffic Routing
        └────────────┬───────────┘
                     │
        ┌────────────┴──────────────┐
        │                           │
        ▼                           ▼
┌──────────────┐           ┌──────────────┐
│    Ghost     │           │    MySQL     │
│  (Port 2368) │           │ (localhost)  │
│  Container   │           │  Container   │
└──────────────┘           └──────────────┘
        │                           │
        └───────────────────────────┘
         (Docker Compose Network)


Optional (für ActivityPub self-hosted):

        ┌────────────────────────┐
        │    CADDY (cont.)       │
        └────────────┬───────────┘
                     │
        ┌────────────┴──────────────────────┐
        │                                   │
        ▼                                   ▼
┌──────────────┐                  ┌──────────────┐
│    Ghost     │                  │  ActivityPub │
│  (2368)      │                  │  (Port 8080) │
└──────────────┘                  └──────────────┘

Step-by-Step Setup

1. Server vorbereiten (Ubuntu 24.04)

# SSH-Keys einrichten (am besten)
# Domain DNS-Record auf Server-IP zeigen

# System updaten
sudo apt update && sudo apt upgrade -y

# Docker installieren (offizielle Methode)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
rm get-docker.sh

# Docker Compose als Plugin (nicht docker-compose!)
sudo apt install -y docker-compose-plugin

# Verifikation
docker --version
docker compose version

# User zur docker-Gruppe hinzufügen (optional, weniger sudo)
sudo usermod -aG docker $USER

2. Ghost Docker Setup

# Verzeichnis erstellen
sudo mkdir -p /opt/ghost && cd /opt/ghost

# Repo klonen
sudo git clone https://github.com/TryGhost/ghost-docker.git . 
# (Punkt = in dieses Verzeichnis)

# Konfiguration vorbereiten
sudo cp .env.example .env
sudo nano .env

In .env eintragen:

DOMAIN=deine-domain.de                # Keine www, keine https!
ADMIN_DOMAIN=                          # Leer lassen (=DOMAIN)

# Sichere Passwörter generieren:
# openssl rand -hex 32
DATABASE_ROOT_PASSWORD=<256bit-hex-passwort>
DATABASE_PASSWORD=<256bit-hex-passwort>

# SMTP für E-Mails (optional aber empfohlen)
MAIL_FROM="Dein Blog <[email protected]>"
MAIL_TRANSPORT=SMTP
MAIL_HOST=smtp.dein-provider.de
MAIL_PORT=587
MAIL_USER=dein-smtp-user
MAIL_PASSWORD=dein-smtp-passwort

# ActivityPub (optional, self-hosted)
COMPOSE_PROFILES=activitypub

3. Caddy Konfiguration

# Caddy config folder
sudo nano caddy/Caddyfile

Minimal Caddyfile (du brauchst fast nichts zu ändern):

deine-domain.de {
    reverse_proxy ghost:2368
    
    # Security Headers (optional, aber empfohlen)
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        Referrer-Policy "no-referrer-when-downgrade"
    }
    
    # Kompression
    encode zstd gzip
}

# Redirect www zu non-www (optional)
www.deine-domain.de {
    redir https://deine-domain.de{uri} permanent
}

# ActivityPub Pfade (nur wenn self-hosted)
deine-domain.de {
    # Ghost main
    reverse_proxy /ghost/admin* ghost:2368
    reverse_proxy /* ghost:2368
    
    # ActivityPub
    reverse_proxy /.ghost/activitypub/* activitypub:8080
    reverse_proxy /.well-known/webfinger activitypub:8080
    reverse_proxy /.well-known/nodeinfo activitypub:8080
}

Das war's. Caddy macht den Rest automatisch:

  • SSL-Zertifikat von Let's Encrypt
  • HTTP → HTTPS Redirect
  • Certificate Renewal
  • All ohne dich zu fragen

4. Starten

cd /opt/ghost

# Bilder pullen
sudo docker compose pull

# Starten
sudo docker compose up -d

# Logs checken (sollte nach ~30sec laufen)
sudo docker compose logs -f ghost

Nach ~30 Sekunden: https://deine-domain.de sollte funktionieren.

Admin Panel: https://deine-domain.de/ghost

5. Backups (kritisch!)

# Täglich um 2 Uhr nachts
# In crontab: crontab -e

0 2 * * * cd /opt/ghost && \
  docker compose exec -T db mysqldump -u root -p$DATABASE_ROOT_PASSWORD --all-databases | \
  gzip > /backups/ghost-db-$(date +\%Y\%m\%d).sql.gz && \
  tar czf /backups/ghost-content-$(date +\%Y\%m\%d).tar.gz ghost-content/

Updates

cd /opt/ghost

# Images updaten
docker compose pull

# Restart
docker compose up -d

# Fertig. Caddy merkt das gar nicht.

Logs & Monitoring

# Ghost Logs
docker compose logs -f ghost

# Caddy Logs
docker compose logs -f caddy

# MySQL
docker compose logs -f db

# Ressourcen
docker stats

ActivityPub (wenn gewünscht)

Nach der Installation:

  1. Admin Panel: /ghost
  2. Settings → Growth → Network
  3. Toggle "Network" an
  4. Handle ist automatisch @[email protected] oder dein Owner-Slug

Das war's. Self-hosted ActivityPub läuft komplett im Container, Caddy routet automatisch.

Warum das die beste Lösung ist

Keine Konflikte: Caddy hat volle Kontrolle, keine Konkurrenz
Automatische Zertifikate: Let's Encrypt wird vollautomatisch verwaltet
Simpel: Ein docker compose.yml, ein Caddyfile
Wartungsarm: Updates sind pull + up -d
Skalierbar: Wenn du später mehr brauchst, ist das Fundament stabil
Zukunftssicher: Ghost 7.0 ist nur noch ein Update
ActivityPub ready: Alles vorkonfiguriert

Performance

Auf einem 1GB VPS:

  • Caddy: ~14MB RAM
  • Ghost: ~100MB RAM
  • MySQL: ~150-200MB RAM
  • Gesamt: ~300-400MB RAM
  • Reste: ~600-700MB für OS/Buffer

Result: Viel Luft, sehr schnell, kein Swapping.

Troubleshooting

Caddy findet SSL-Zertifikat nicht:
→ DNS-Record ist kaputt, warte 5 Min oder check docker compose logs caddy

Ghost zeigt "Wrong URL":
→ In .env: DOMAIN=deine-domain.de (KEINE https://)

ActivityPub funktioniert nicht:
→ Check Caddyfile: Alle 3 Pfade zu Port 8080?

Backup/Restore:
docker compose stop → Backups ersetzen → docker compose up -d

Fazit

Das ist der saubere, "DevOps-richtige" Weg für einen Self-Hoster, der keine Systeme nebeneinander jonglieren will. Ein Server, eine App, volle Kontrolle.