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

AnforderungMinimum
Docker20.10+
Docker Composev2 (das docker compose-Plugin)
RAM4 GB (8 GB empfohlen)
Festplatte20 GB frei
opensslBeliebige Version (zum Erzeugen von Secrets)
Domain + DNS-KontrolleFü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.

RollemacOSLinuxWindows
Desktop-AppUnterstützt (universal)UnterstütztUnterstützt
Selbst gehosteter ServerNicht unterstützt (nur zur Evaluierung via Docker Desktop)UnterstütztNicht unterstützt (nur zur Evaluierung via Docker Desktop)
Contributor-/Dev-CheckoutUnterstütztUnterstütztUnterstü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
Version für reproduzierbare / auditierbare Installationen pinnen

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.

Lieber eine grafische Oberfläche?

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.

Nicht im Self-Hosting enthalten

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.

Alter Bash-Assistent

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
Wie der Assistent es macht

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-deploydocker 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
Admin-Konto

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

ServicePort(s)Rolle
Convex3210, 3211Datenbank, Echtzeit-Subscriptions, Serverless-Functions, Dateispeicher
Web3000Nuxt-Anwendung — Dashboard, E-Mail-Builder, Einstellungen
MTA3100, 25Mail 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
Redisnur intern (6379)Job-Queue und Rate-Limiting-Zustand für den MTA
ClamAVnur intern (3310)Virenprüfung für E-Mail-Anhänge. Profilgesteuert (clamav) und nicht in den Standard-COMPOSE_PROFILES enthalten — siehe Hinweis unten
Updaternur 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
Caddy80, 443Optionaler Reverse Proxy mit automatischem HTTPS, aktivierbar über --profile tls (siehe Caddyfile.example)
`docker compose up -d` startet nur die Profile in COMPOSE_PROFILES

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 aus apps/api an 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 dev verfü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 mit docker compose --profile dashboard up -d convex-dashboard. Sie bindet nur an 127.0.0.1; öffnen Sie auf einem entfernten VPS daher einen SSH-Tunnel (ssh -L 6791:127.0.0.1:6791 host), bevor Sie http://localhost:6791 aufrufen.

Einen tieferen Einblick in die Architektur finden Sie unter Self-Hosting-Architektur.

Wie geht es weiter