Self-Hosting
Owlat mit Docker Compose auf Ihrer eigenen Infrastruktur betreiben. Vollständige Anleitung vom ersten Start bis zur Produktion.
Owlat läuft vollständig auf Ihrer eigenen Infrastruktur über eine einzige Docker-Compose-Datei. Cloud-Dienste sind nicht erforderlich — der Stack bringt eine eigene Datenbank und Echtzeit-Engine mit und kann seinen eigenen Mailserver (MTA) sowie einen Virenscanner (ClamAV) betreiben. Mailserver und Virenscanner sind über Docker-Compose-Profile (mta und clamav) abgesichert; welche davon laufen, hängt also von Ihrem COMPOSE_PROFILES ab — siehe Stack-Überblick.
Warum Self-Hosting
- Datensouveränität — Ihre E-Mails, Kontakte und Analysedaten verlassen niemals Ihre Server
- Compliance — Anforderungen der DSGVO, von HIPAA oder branchenspezifische Vorgaben zur Datenlokalisierung erfüllen
- Air-Gapped-Umgebungen — Betrieb ganz ohne externe Netzwerkabhängigkeiten
- Volle Kontrolle — jede Komponente anpassen, auditieren und erweitern
Voraussetzungen
| Anforderung | Minimum |
|---|---|
| Docker | 20.10+ |
| Docker Compose | v2 (das docker compose-Plugin) |
| RAM | 4 GB (8 GB empfohlen) |
| Festplatte | 20 GB frei |
| openssl | Beliebige Version (zum Erzeugen von Secrets) |
| Domain + DNS-Kontrolle | Für den produktiven E-Mail-Versand erforderlich |
Unterstützte Plattformen
Owlat läuft auf unterschiedlichen Betriebssystemen — je nachdem, welchen Teil Sie meinen: den Desktop-Client, den selbst gehosteten Server oder ein Checkout zum Mitentwickeln.
| Rolle | macOS | Linux | Windows |
|---|---|---|---|
| Desktop-App | Unterstützt (universal) | Unterstützt | Unterstützt |
| Selbst gehosteter Server | Nicht unterstützt (nur zur Evaluierung via Docker Desktop) | Unterstützt | Nicht unterstützt (nur zur Evaluierung via Docker Desktop) |
| Contributor-/Dev-Checkout | Unterstützt | Unterstützt | Unterstützt über WSL2 oder Git Bash |
Desktop-App
Die Desktop-App wird für macOS (Universal Binary), Linux und Windows ausgeliefert. Unter Linux benötigen Endanwender einige System-Laufzeitbibliotheken, damit die WebKit-basierte Shell startet:
- Debian / Ubuntu:
libwebkit2gtk-4.1-0,librsvg2-2 - Fedora:
webkit2gtk4.1,librsvg2
macOS und Windows bringen alles Nötige mit, dort sind keine zusätzlichen Pakete erforderlich.
Selbst gehosteter Server
Der selbst gehostete Server läuft konzeptbedingt ausschließlich unter Linux. Er setzt auf systemd und Docker Engine und belegt die üblichen Mail- und Web-Ports (25, 143, 443, 993). Der empfohlene Installationsweg führt über einen frischen Linux-VPS.
macOS und Windows werden als Server-Host nicht unterstützt. Für eine lokale Evaluierung auf diesen Systemen verwenden Sie Docker Desktop mit dem manuellen docker compose-Ablauf — damit laufen dieselben Container, ohne dass ein Linux-Host nötig ist.
Contributor-/Dev-Checkout
Das Mitentwickeln funktioniert nativ unter macOS und Linux. Unter Windows betreiben Sie das Repository in WSL2 oder Git Bash, da die Lint- und Verifikations-Gates Bash-Skripte sind. Das vollständige Entwicklungs-Setup finden Sie unter CONTRIBUTING.
Der schnellste Weg
Auf einem frischen Linux-VPS mit installiertem Docker + Docker Compose v2:
curl -fsSL https://get.owlat.app | bash
Der Einzeiler klont das Repository, führt Preflight-Checks aus, installiert das owlat-CLI nach /usr/local/bin und übergibt an den containerisierten owlat quickstart-Assistenten. Dieser eine Ablauf erzeugt Secrets, schreibt Ihre .env, startet den Docker-Stack, erstellt den Convex-Admin-Key, deployt die Functions, setzt die Convex-Laufzeit-Umgebungsvariablen und legt Ihr Admin-Konto an — ohne Bun oder Node auf dem Host. Nicht-interaktive Nutzung:
# Skip prompts, accept all defaults
OWLAT_ASSUME_YES=1 curl -fsSL https://get.owlat.app | bash
# Read answers from a config file (for CI / Ansible)
OWLAT_CONFIG_FILE=/path/to/answers.env curl -fsSL https://get.owlat.app | bash
# Pin a specific release tag for a reproducible install
OWLAT_REF=v0.1.0 curl -fsSL https://get.owlat.app | bash
Standardmäßig löst der Installer auf das zuletzt veröffentlichte Release-Tag auf (nicht auf einen wandernden Branch) — curl | bash bittet einmalig um Ihr Vertrauen, und dieses Vertrauen sollte nicht stillschweigend auf jeden künftigen Commit ausgeweitet werden. Setzen Sie OWLAT_REF auf ein Tag (z. B. v0.1.0), damit Wiederholungsläufe und CI byteweise denselben Code installieren, den Sie geprüft haben; OWLAT_REF=main nur dann, wenn Sie ausdrücklich den allerneuesten Stand wollen.
Die Desktop-App kann einen frischen Server für Sie über SSH installieren — ganz ohne Terminal — mit einer Live-Zeitleiste des Provisionierungsfortschritts. Sie steuert denselben Installer aus der Ferne.
Stripe-Abrechnung, Hetzner-VPS-Provisionierung und Tarifverwaltung gehören zur Control Plane der Managed Cloud (eine separate, private Codebasis) und sind nicht Teil der quelloffenen Self-Hosting-Distribution. Self-Hoster betreiben den Owlat-Stack direkt mit docker compose up -d.
Schnellstart
Option A: owlat quickstart (empfohlen)
Der empfohlene Ablauf beim ersten Start ist der containerisierte quickstart-Assistent. Er führt das Setup-CLI (gebaut aus apps/setup-cli/) im Docker-Image wolvesdotink/setup aus, sodass auf dem Host nur Docker und Docker Compose v2 vorausgesetzt werden. Ein Befehl erledigt das komplette Deployment: Konfigurationsassistent → docker compose up -d → Erstellen des Admin-Keys → Function-Deployment → Convex-Umgebungsvariablen → Admin-Bootstrap.
git clone https://github.com/wolvesdotink/owlat.git
cd owlat
scripts/owlat quickstart
Wenn Sie den Einzeiler oben ausgeführt haben, liegt das owlat-CLI bereits in Ihrem PATH, Sie können also einfach owlat quickstart aufrufen. Der komplette Ablauf dauert vom leeren VPS bis zur funktionierenden Installation rund 10 Minuten und erledigt alles aus Option B weiter unten automatisch — einschließlich des Übertragens der Convex-Laufzeit-Umgebungsvariablen (BETTER_AUTH_SECRET, SITE_URL, UNSUBSCRIBE_SECRET, EMAIL_PROVIDER, MTA_API_URL sowie der Absenderidentität) in das Deployment, damit Auth-Einladungen und der erste Versand ohne weitere Schritte funktionieren.
Der ältere, reine Bash-Assistent wird weiterhin ausgeliefert und funktioniert: bash scripts/setup.sh (wählen Sie bei der Abfrage „Self-Hosted (Docker Compose)“). Er ist der Legacy-Pfad und über das CLI mit owlat quickstart --legacy oder durch Setzen von OWLAT_LEGACY_WIZARD=1 vor dem Einzeiler erreichbar. Für neue Installationen ist owlat quickstart vorzuziehen.
Option B: Manuelles Setup
1. Klonen und konfigurieren
git clone https://github.com/wolvesdotink/owlat.git
cd owlat
cp .env.selfhost.example .env
2. Secrets erzeugen
# Instance secret (hex)
openssl rand -hex 32
# MTA API key and webhook secret (base64)
openssl rand -base64 32
openssl rand -base64 32
Tragen Sie die erzeugten Werte in der .env unter INSTANCE_SECRET, MTA_API_KEY und MTA_WEBHOOK_SECRET ein.
3. Stack starten
docker compose up -d
4. Warten, bis Convex bereit ist
# Watch logs until you see "ready" or "listening"
docker compose logs -f convex
# Press Ctrl+C once ready
5. Admin-Key erzeugen
docker compose exec convex ./generate_admin_key.sh
Kopieren Sie den ausgegebenen Key und fügen Sie ihn Ihrer .env hinzu:
# In .env, set:
CONVEX_ADMIN_KEY=<paste-key-here>
Ein Neustart ist nicht nötig — die dauerhaft laufenden Services lesen CONVEX_ADMIN_KEY nicht. Nur der einmalig laufende Container convex-deploy (nächster Schritt) und die optionalen Sidecars verwenden ihn, und diese lesen die .env bei jedem Lauf neu ein.
6. Functions deployen
# Deploy the main Convex functions
docker compose --profile deploy run --rm convex-deploy
7. Convex-Umgebungsvariablen setzen
Dies sind Variablen auf Anwendungsebene, die von den Serverless-Functions gelesen werden. Setzen Sie sie mit dem Convex-CLI und Ihrem Admin-Key:
export CONVEX_URL=http://localhost:3210
export CONVEX_ADMIN_KEY=<your-admin-key>
npx convex env set BETTER_AUTH_SECRET "$(openssl rand -base64 32)" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY
npx convex env set UNSUBSCRIBE_SECRET "$(openssl rand -base64 32)" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY
npx convex env set SITE_URL "http://localhost:3000" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY
# Do NOT set CONVEX_SITE_URL here — it is a Convex BUILT-IN and `convex env set`
# rejects it (EnvVarNameForbidden). The backend derives it from
# CONVEX_SITE_ORIGIN on the convex container, which docker-compose interpolates
# from NUXT_PUBLIC_CONVEX_SITE_URL in your .env.
npx convex env set EMAIL_PROVIDER "mta" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY
npx convex env set MTA_API_URL "http://mta:3100" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY
npx convex env set MTA_API_KEY "<your-mta-api-key>" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY
npx convex env set MTA_WEBHOOK_SECRET "<your-mta-webhook-secret>" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY
npx convex env set DEFAULT_FROM_EMAIL "noreply@example.com" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY
npx convex env set DEFAULT_FROM_NAME "Owlat" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY
npx convex env set DEFAULT_FROM_DOMAIN "example.com" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY
Die obigen npx convex env set-Befehle auf dem Host sind ein manuelles Äquivalent. Das unterstützte Tooling (apps/setup-cli/src/lib/convexDeploy.ts) überträgt diese Variablen stattdessen über den Container convex-deploy — docker compose --profile deploy run --rm convex-deploy —, der das gepinnte Convex-CLI und CONVEX_SELF_HOSTED_URL=http://convex:3210 bereits mitbringt. Convex-Functions lesen diese Werte aus dem Deployment, nicht aus der .env von Compose.
8. Dashboard öffnen
open http://localhost:3000
Der Setup-Assistent legt automatisch ein Admin-Konto an. Beim manuellen Setup starten Sie zunächst das profilgesteuerte Dashboard mit docker compose --profile dashboard up -d convex-dashboard (es bindet nur an 127.0.0.1 — bei entfernten Hosts tunneln Sie mit ssh -L 6791:127.0.0.1:6791 host) und öffnen dann http://localhost:6791, um Ihren ersten Benutzer anzulegen; alternativ rufen Sie den im Setup-Skript dokumentierten Seed-Endpunkt auf.
Stack-Überblick
| Service | Port(s) | Rolle |
|---|---|---|
| Convex | 3210, 3211 | Datenbank, Echtzeit-Subscriptions, Serverless-Functions, Dateispeicher |
| Web | 3000 | Nuxt-Anwendung — Dashboard, E-Mail-Builder, Einstellungen |
| MTA | 3100, 25 | Mail Transfer Agent — SMTP-Zustellung, Bounce-Verarbeitung, IP-Warming. Profilgesteuert (mta); die Standarddatei .env.selfhost.example liefert COMPOSE_PROFILES=mta aus, der Dienst läuft also bei einem Standard-Self-Host, wird aber übersprungen, wenn Sie auf einen Drittanbieter (Resend/SES) umstellen |
| Redis | nur intern (6379) | Job-Queue und Rate-Limiting-Zustand für den MTA |
| ClamAV | nur intern (3310) | Virenprüfung für E-Mail-Anhänge. Profilgesteuert (clamav) und nicht in den Standard-COMPOSE_PROFILES enthalten — siehe Hinweis unten |
| Updater | nur intern (3200) | Sidecar für Selbst-Updates aus der App heraus — führt stellvertretend für die Web-App docker compose pull && up -d aus. Bindet ausschließlich OWLAT_INSTALL_DIR ein; der Docker-Socket wird nicht mehr eingebunden, stattdessen wird Docker über den Least-Privilege-docker-socket-proxy gesteuert (DOCKER_HOST=tcp://docker-socket-proxy:2375), den einzigen Container, der /var/run/docker.sock einbindet (schreibgeschützt). Erreichbar über das Docker-Netzwerk unter http://updater:3200 |
| Caddy | 80, 443 | Optionaler Reverse Proxy mit automatischem HTTPS, aktivierbar über --profile tls (siehe Caddyfile.example) |
docker compose up -d berücksichtigt COMPOSE_PROFILES aus Ihrer .env: Es startet die dauerhaft laufenden Basis-Services plus die dort aufgeführten Profile und überspringt jeden profilgesteuerten Service, der nicht aufgeführt ist. Die Standarddatei .env.selfhost.example liefert COMPOSE_PROFILES=mta aus — damit startet der MTA, aber nicht ClamAV. Auch wenn das Feature-Flag scan.files standardmäßig aktiv ist und die Anhangsprüfung fail-open arbeitet (Mail wird auch zugestellt, wenn der Scanner nicht erreichbar ist), startet ClamAV bei einer Standardinstallation nie — Anhänge werden also ungeprüft zugestellt. Damit Dateien tatsächlich geprüft werden, ergänzen Sie clamav in COMPOSE_PROFILES (z. B. COMPOSE_PROFILES=mta,clamav) und führen docker compose up -d erneut aus, oder aktivieren Sie das Paket scan.files über den Assistenten, der das Profil clamav für Sie einbindet.
Einige weitere Services laufen nur bei Bedarf und nicht als Teil des dauerhaft laufenden Stacks:
convex-deploy(--profile deploy) — einmalig laufender Container, der die Functions, das Schema und die HTTP-Routen ausapps/apian das Backend überträgt und die Convex-Laufzeit-Umgebungsvariablen setzt. Führen Sie ihn nach dem ersten Start und nach jedem Image-Pull aus:docker compose --profile deploy run --rm convex-deploy.code-worker(--profile inbox-codetasks, ebenfalls über--profile devverfügbar) — der optionale Sidecar für den KI-Coding-Agenten; standardmäßig aus.convex-dashboard(--profile dashboard) — Admin-Oberfläche zum Inspizieren von Daten und Debuggen von Functions. Starten Sie sie mitdocker compose --profile dashboard up -d convex-dashboard. Sie bindet nur an127.0.0.1; öffnen Sie auf einem entfernten VPS daher einen SSH-Tunnel (ssh -L 6791:127.0.0.1:6791 host), bevor Siehttp://localhost:6791aufrufen.
Einen tieferen Einblick in die Architektur finden Sie unter Self-Hosting-Architektur.
Wie geht es weiter
- Konfigurationsreferenz — alle Umgebungsvariablen, Service-Topologie und Volumes
- DNS- & E-Mail-Setup — SPF, DKIM, DMARC und Bounce-Verarbeitung für den produktiven E-Mail-Versand
- Produktions-Deployment — Reverse Proxy, TLS, Firewall, Backups und Monitoring
- Wartung & Updates — Ihre Instanz aktuell halten und Fehler beheben