[{"data":1,"prerenderedAt":1667},["ShallowReactive",2],{"search-de":3,"content-de-developer\u002Fself-hosting":4,"surround-de-\u002Fdeveloper\u002Fself-hosting":1659},[],{"id":5,"title":6,"body":7,"description":1652,"extension":1653,"meta":1654,"navigation":403,"path":1655,"seo":1656,"stem":1657,"__hash__":1658},"content_de\u002F3.developer\u002F30.self-hosting.md","Self-Hosting",{"type":8,"value":9,"toc":1635},"minimark",[10,33,38,67,71,143,147,150,213,217,220,246,249,252,264,285,288,304,308,311,344,363,464,490,501,510,514,521,539,573,602,623,627,632,662,667,720,736,741,760,765,795,800,820,826,850,863,868,899,904,907,1242,1270,1275,1288,1310,1313,1473,1531,1534,1590,1597,1601,1631],[11,12,13,14,18,19,22,23,26,27,32],"p",{},"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 (",[15,16,17],"code",{},"mta"," und ",[15,20,21],{},"clamav",") abgesichert; welche davon laufen, hängt also von Ihrem ",[15,24,25],{},"COMPOSE_PROFILES"," ab — siehe ",[28,29,31],"a",{"href":30},"#stack-overview","Stack-Überblick",".",[34,35,37],"h2",{"id":36},"warum-self-hosting","Warum Self-Hosting",[39,40,41,49,55,61],"ul",{},[42,43,44,48],"li",{},[45,46,47],"strong",{},"Datensouveränität"," — Ihre E-Mails, Kontakte und Analysedaten verlassen niemals Ihre Server",[42,50,51,54],{},[45,52,53],{},"Compliance"," — Anforderungen der DSGVO, von HIPAA oder branchenspezifische Vorgaben zur Datenlokalisierung erfüllen",[42,56,57,60],{},[45,58,59],{},"Air-Gapped-Umgebungen"," — Betrieb ganz ohne externe Netzwerkabhängigkeiten",[42,62,63,66],{},[45,64,65],{},"Volle Kontrolle"," — jede Komponente anpassen, auditieren und erweitern",[34,68,70],{"id":69},"voraussetzungen","Voraussetzungen",[72,73,74,87],"table",{},[75,76,77],"thead",{},[78,79,80,84],"tr",{},[81,82,83],"th",{},"Anforderung",[81,85,86],{},"Minimum",[88,89,90,99,111,119,127,135],"tbody",{},[78,91,92,96],{},[93,94,95],"td",{},"Docker",[93,97,98],{},"20.10+",[78,100,101,104],{},[93,102,103],{},"Docker Compose",[93,105,106,107,110],{},"v2 (das ",[15,108,109],{},"docker compose","-Plugin)",[78,112,113,116],{},[93,114,115],{},"RAM",[93,117,118],{},"4 GB (8 GB empfohlen)",[78,120,121,124],{},[93,122,123],{},"Festplatte",[93,125,126],{},"20 GB frei",[78,128,129,132],{},[93,130,131],{},"openssl",[93,133,134],{},"Beliebige Version (zum Erzeugen von Secrets)",[78,136,137,140],{},[93,138,139],{},"Domain + DNS-Kontrolle",[93,141,142],{},"Für den produktiven E-Mail-Versand erforderlich",[34,144,146],{"id":145},"unterstützte-plattformen","Unterstützte Plattformen",[11,148,149],{},"Owlat läuft auf unterschiedlichen Betriebssystemen — je nachdem, welchen Teil Sie meinen: den Desktop-Client, den selbst gehosteten Server oder ein Checkout zum Mitentwickeln.",[72,151,152,168],{},[75,153,154],{},[78,155,156,159,162,165],{},[81,157,158],{},"Rolle",[81,160,161],{},"macOS",[81,163,164],{},"Linux",[81,166,167],{},"Windows",[88,169,170,185,199],{},[78,171,172,177,180,183],{},[93,173,174],{},[45,175,176],{},"Desktop-App",[93,178,179],{},"Unterstützt (universal)",[93,181,182],{},"Unterstützt",[93,184,182],{},[78,186,187,192,195,197],{},[93,188,189],{},[45,190,191],{},"Selbst gehosteter Server",[93,193,194],{},"Nicht unterstützt (nur zur Evaluierung via Docker Desktop)",[93,196,182],{},[93,198,194],{},[78,200,201,206,208,210],{},[93,202,203],{},[45,204,205],{},"Contributor-\u002FDev-Checkout",[93,207,182],{},[93,209,182],{},[93,211,212],{},"Unterstützt über WSL2 oder Git Bash",[214,215,176],"h3",{"id":216},"desktop-app",[11,218,219],{},"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:",[39,221,222,235],{},[42,223,224,227,228,231,232],{},[45,225,226],{},"Debian \u002F Ubuntu",": ",[15,229,230],{},"libwebkit2gtk-4.1-0",", ",[15,233,234],{},"librsvg2-2",[42,236,237,227,240,231,243],{},[45,238,239],{},"Fedora",[15,241,242],{},"webkit2gtk4.1",[15,244,245],{},"librsvg2",[11,247,248],{},"macOS und Windows bringen alles Nötige mit, dort sind keine zusätzlichen Pakete erforderlich.",[214,250,191],{"id":251},"selbst-gehosteter-server",[11,253,254,255,258,259,263],{},"Der selbst gehostete Server läuft konzeptbedingt ",[45,256,257],{},"ausschließlich unter Linux",". Er setzt auf systemd und Docker Engine und belegt die üblichen Mail- und Web-Ports (25, 143, 443, 993). Der ",[28,260,262],{"href":261},"#fastest-path","empfohlene Installationsweg"," führt über einen frischen Linux-VPS.",[11,265,266,267,270,271,277,278,284],{},"macOS und Windows werden als Server-Host ",[45,268,269],{},"nicht"," unterstützt. Für eine lokale Evaluierung auf diesen Systemen verwenden Sie ",[28,272,276],{"href":273,"rel":274},"https:\u002F\u002Fdocs.docker.com\u002Fdesktop\u002F",[275],"nofollow","Docker Desktop"," mit dem manuellen ",[28,279,281,283],{"href":280},"#option-b-manual-setup",[15,282,109],{},"-Ablauf"," — damit laufen dieselben Container, ohne dass ein Linux-Host nötig ist.",[214,286,205],{"id":287},"contributor-dev-checkout",[11,289,290,291,294,295,298,299,32],{},"Das Mitentwickeln funktioniert nativ unter macOS und Linux. Unter Windows betreiben Sie das Repository in ",[45,292,293],{},"WSL2"," oder ",[45,296,297],{},"Git Bash",", da die Lint- und Verifikations-Gates Bash-Skripte sind. Das vollständige Entwicklungs-Setup finden Sie unter ",[28,300,303],{"href":301,"rel":302},"https:\u002F\u002Fgithub.com\u002Fwolvesdotink\u002Fowlat\u002Fblob\u002Fmain\u002FCONTRIBUTING.md",[275],"CONTRIBUTING",[34,305,307],{"id":306},"der-schnellste-weg","Der schnellste Weg",[11,309,310],{},"Auf einem frischen Linux-VPS mit installiertem Docker + Docker Compose v2:",[312,313,318],"pre",{"className":314,"code":315,"language":316,"meta":317,"style":317},"language-sh shiki shiki-themes github-light github-dark-dimmed","curl -fsSL https:\u002F\u002Fget.owlat.app | bash\n","sh","",[15,319,320],{"__ignoreMap":317},[321,322,325,329,333,337,341],"span",{"class":323,"line":324},"line",1,[321,326,328],{"class":327},"sOLd2","curl",[321,330,332],{"class":331},"sviXB"," -fsSL",[321,334,336],{"class":335},"s-HuK"," https:\u002F\u002Fget.owlat.app",[321,338,340],{"class":339},"s7YZ4"," |",[321,342,343],{"class":327}," bash\n",[11,345,346,347,350,351,354,355,358,359,362],{},"Der Einzeiler klont das Repository, führt Preflight-Checks aus, installiert das ",[15,348,349],{},"owlat","-CLI nach ",[15,352,353],{},"\u002Fusr\u002Flocal\u002Fbin"," und übergibt an den containerisierten ",[15,356,357],{},"owlat quickstart","-Assistenten. Dieser eine Ablauf erzeugt Secrets, schreibt Ihre ",[15,360,361],{},".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:",[312,364,366],{"className":314,"code":365,"language":316,"meta":317,"style":317},"# Skip prompts, accept all defaults\nOWLAT_ASSUME_YES=1 curl -fsSL https:\u002F\u002Fget.owlat.app | bash\n\n# Read answers from a config file (for CI \u002F Ansible)\nOWLAT_CONFIG_FILE=\u002Fpath\u002Fto\u002Fanswers.env curl -fsSL https:\u002F\u002Fget.owlat.app | bash\n\n# Pin a specific release tag for a reproducible install\nOWLAT_REF=v0.1.0 curl -fsSL https:\u002F\u002Fget.owlat.app | bash\n",[15,367,368,374,398,405,411,432,437,443],{"__ignoreMap":317},[321,369,370],{"class":323,"line":324},[321,371,373],{"class":372},"sDN9O","# Skip prompts, accept all defaults\n",[321,375,377,381,384,387,390,392,394,396],{"class":323,"line":376},2,[321,378,380],{"class":379},"sYgZi","OWLAT_ASSUME_YES",[321,382,383],{"class":339},"=",[321,385,386],{"class":335},"1",[321,388,389],{"class":327}," curl",[321,391,332],{"class":331},[321,393,336],{"class":335},[321,395,340],{"class":339},[321,397,343],{"class":327},[321,399,401],{"class":323,"line":400},3,[321,402,404],{"emptyLinePlaceholder":403},true,"\n",[321,406,408],{"class":323,"line":407},4,[321,409,410],{"class":372},"# Read answers from a config file (for CI \u002F Ansible)\n",[321,412,414,417,419,422,424,426,428,430],{"class":323,"line":413},5,[321,415,416],{"class":379},"OWLAT_CONFIG_FILE",[321,418,383],{"class":339},[321,420,421],{"class":335},"\u002Fpath\u002Fto\u002Fanswers.env",[321,423,389],{"class":327},[321,425,332],{"class":331},[321,427,336],{"class":335},[321,429,340],{"class":339},[321,431,343],{"class":327},[321,433,435],{"class":323,"line":434},6,[321,436,404],{"emptyLinePlaceholder":403},[321,438,440],{"class":323,"line":439},7,[321,441,442],{"class":372},"# Pin a specific release tag for a reproducible install\n",[321,444,446,449,451,454,456,458,460,462],{"class":323,"line":445},8,[321,447,448],{"class":379},"OWLAT_REF",[321,450,383],{"class":339},[321,452,453],{"class":335},"v0.1.0",[321,455,389],{"class":327},[321,457,332],{"class":331},[321,459,336],{"class":335},[321,461,340],{"class":339},[321,463,343],{"class":327},[465,466,469],"callout",{"title":467,"type":468},"Version für reproduzierbare \u002F auditierbare Installationen pinnen","info",[11,470,471,472,475,476,479,480,482,483,485,486,489],{},"Standardmäßig löst der Installer auf das zuletzt veröffentlichte ",[45,473,474],{},"Release-Tag"," auf (nicht auf einen wandernden Branch) — ",[15,477,478],{},"curl | bash"," bittet einmalig um Ihr Vertrauen, und dieses Vertrauen sollte nicht stillschweigend auf jeden künftigen Commit ausgeweitet werden. Setzen Sie ",[15,481,448],{}," auf ein Tag (z. B. ",[15,484,453],{},"), damit Wiederholungsläufe und CI byteweise denselben Code installieren, den Sie geprüft haben; ",[15,487,488],{},"OWLAT_REF=main"," nur dann, wenn Sie ausdrücklich den allerneuesten Stand wollen.",[465,491,493],{"title":492,"type":468},"Lieber eine grafische Oberfläche?",[11,494,495,496,500],{},"Die ",[28,497,499],{"href":498},"\u002Fdeveloper\u002Fself-hosting-desktop","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.",[465,502,504],{"title":503,"type":468},"Nicht im Self-Hosting enthalten",[11,505,506,507,32],{},"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 ",[15,508,509],{},"docker compose up -d",[34,511,513],{"id":512},"schnellstart","Schnellstart",[214,515,517,518,520],{"id":516},"option-a-owlat-quickstart-empfohlen","Option A: ",[15,519,357],{}," (empfohlen)",[11,522,523,524,527,528,531,532,535,536,538],{},"Der empfohlene Ablauf beim ersten Start ist der containerisierte ",[15,525,526],{},"quickstart","-Assistent. Er führt das Setup-CLI (gebaut aus ",[15,529,530],{},"apps\u002Fsetup-cli\u002F",") im Docker-Image ",[15,533,534],{},"wolvesdotink\u002Fsetup"," aus, sodass auf dem Host nur Docker und Docker Compose v2 vorausgesetzt werden. Ein Befehl erledigt das komplette Deployment: Konfigurationsassistent → ",[15,537,509],{}," → Erstellen des Admin-Keys → Function-Deployment → Convex-Umgebungsvariablen → Admin-Bootstrap.",[312,540,544],{"className":541,"code":542,"language":543,"meta":317,"style":317},"language-bash shiki shiki-themes github-light github-dark-dimmed","git clone https:\u002F\u002Fgithub.com\u002Fwolvesdotink\u002Fowlat.git\ncd owlat\nscripts\u002Fowlat quickstart\n","bash",[15,545,546,557,565],{"__ignoreMap":317},[321,547,548,551,554],{"class":323,"line":324},[321,549,550],{"class":327},"git",[321,552,553],{"class":335}," clone",[321,555,556],{"class":335}," https:\u002F\u002Fgithub.com\u002Fwolvesdotink\u002Fowlat.git\n",[321,558,559,562],{"class":323,"line":376},[321,560,561],{"class":331},"cd",[321,563,564],{"class":335}," owlat\n",[321,566,567,570],{"class":323,"line":400},[321,568,569],{"class":327},"scripts\u002Fowlat",[321,571,572],{"class":335}," quickstart\n",[11,574,575,576,578,579,582,583,585,586,231,589,231,592,231,595,231,598,601],{},"Wenn Sie den Einzeiler oben ausgeführt haben, liegt das ",[15,577,349],{},"-CLI bereits in Ihrem ",[15,580,581],{},"PATH",", Sie können also einfach ",[15,584,357],{}," 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 (",[15,587,588],{},"BETTER_AUTH_SECRET",[15,590,591],{},"SITE_URL",[15,593,594],{},"UNSUBSCRIBE_SECRET",[15,596,597],{},"EMAIL_PROVIDER",[15,599,600],{},"MTA_API_URL"," sowie der Absenderidentität) in das Deployment, damit Auth-Einladungen und der erste Versand ohne weitere Schritte funktionieren.",[465,603,605],{"title":604,"type":468},"Alter Bash-Assistent",[11,606,607,608,611,612,615,616,619,620,622],{},"Der ältere, reine Bash-Assistent wird weiterhin ausgeliefert und funktioniert: ",[15,609,610],{},"bash scripts\u002Fsetup.sh"," (wählen Sie bei der Abfrage „Self-Hosted (Docker Compose)“). Er ist der Legacy-Pfad und über das CLI mit ",[15,613,614],{},"owlat quickstart --legacy"," oder durch Setzen von ",[15,617,618],{},"OWLAT_LEGACY_WIZARD=1"," vor dem Einzeiler erreichbar. Für neue Installationen ist ",[15,621,357],{}," vorzuziehen.",[214,624,626],{"id":625},"option-b-manuelles-setup","Option B: Manuelles Setup",[11,628,629],{},[45,630,631],{},"1. Klonen und konfigurieren",[312,633,635],{"className":541,"code":634,"language":543,"meta":317,"style":317},"git clone https:\u002F\u002Fgithub.com\u002Fwolvesdotink\u002Fowlat.git\ncd owlat\ncp .env.selfhost.example .env\n",[15,636,637,645,651],{"__ignoreMap":317},[321,638,639,641,643],{"class":323,"line":324},[321,640,550],{"class":327},[321,642,553],{"class":335},[321,644,556],{"class":335},[321,646,647,649],{"class":323,"line":376},[321,648,561],{"class":331},[321,650,564],{"class":335},[321,652,653,656,659],{"class":323,"line":400},[321,654,655],{"class":327},"cp",[321,657,658],{"class":335}," .env.selfhost.example",[321,660,661],{"class":335}," .env\n",[11,663,664],{},[45,665,666],{},"2. Secrets erzeugen",[312,668,670],{"className":541,"code":669,"language":543,"meta":317,"style":317},"# Instance secret (hex)\nopenssl rand -hex 32\n\n# MTA API key and webhook secret (base64)\nopenssl rand -base64 32\nopenssl rand -base64 32\n",[15,671,672,677,690,694,699,710],{"__ignoreMap":317},[321,673,674],{"class":323,"line":324},[321,675,676],{"class":372},"# Instance secret (hex)\n",[321,678,679,681,684,687],{"class":323,"line":376},[321,680,131],{"class":327},[321,682,683],{"class":335}," rand",[321,685,686],{"class":331}," -hex",[321,688,689],{"class":331}," 32\n",[321,691,692],{"class":323,"line":400},[321,693,404],{"emptyLinePlaceholder":403},[321,695,696],{"class":323,"line":407},[321,697,698],{"class":372},"# MTA API key and webhook secret (base64)\n",[321,700,701,703,705,708],{"class":323,"line":413},[321,702,131],{"class":327},[321,704,683],{"class":335},[321,706,707],{"class":331}," -base64",[321,709,689],{"class":331},[321,711,712,714,716,718],{"class":323,"line":434},[321,713,131],{"class":327},[321,715,683],{"class":335},[321,717,707],{"class":331},[321,719,689],{"class":331},[11,721,722,723,725,726,231,729,18,732,735],{},"Tragen Sie die erzeugten Werte in der ",[15,724,361],{}," unter ",[15,727,728],{},"INSTANCE_SECRET",[15,730,731],{},"MTA_API_KEY",[15,733,734],{},"MTA_WEBHOOK_SECRET"," ein.",[11,737,738],{},[45,739,740],{},"3. Stack starten",[312,742,744],{"className":541,"code":743,"language":543,"meta":317,"style":317},"docker compose up -d\n",[15,745,746],{"__ignoreMap":317},[321,747,748,751,754,757],{"class":323,"line":324},[321,749,750],{"class":327},"docker",[321,752,753],{"class":335}," compose",[321,755,756],{"class":335}," up",[321,758,759],{"class":331}," -d\n",[11,761,762],{},[45,763,764],{},"4. Warten, bis Convex bereit ist",[312,766,768],{"className":541,"code":767,"language":543,"meta":317,"style":317},"# Watch logs until you see \"ready\" or \"listening\"\ndocker compose logs -f convex\n# Press Ctrl+C once ready\n",[15,769,770,775,790],{"__ignoreMap":317},[321,771,772],{"class":323,"line":324},[321,773,774],{"class":372},"# Watch logs until you see \"ready\" or \"listening\"\n",[321,776,777,779,781,784,787],{"class":323,"line":376},[321,778,750],{"class":327},[321,780,753],{"class":335},[321,782,783],{"class":335}," logs",[321,785,786],{"class":331}," -f",[321,788,789],{"class":335}," convex\n",[321,791,792],{"class":323,"line":400},[321,793,794],{"class":372},"# Press Ctrl+C once ready\n",[11,796,797],{},[45,798,799],{},"5. Admin-Key erzeugen",[312,801,803],{"className":541,"code":802,"language":543,"meta":317,"style":317},"docker compose exec convex .\u002Fgenerate_admin_key.sh\n",[15,804,805],{"__ignoreMap":317},[321,806,807,809,811,814,817],{"class":323,"line":324},[321,808,750],{"class":327},[321,810,753],{"class":335},[321,812,813],{"class":335}," exec",[321,815,816],{"class":335}," convex",[321,818,819],{"class":335}," .\u002Fgenerate_admin_key.sh\n",[11,821,822,823,825],{},"Kopieren Sie den ausgegebenen Key und fügen Sie ihn Ihrer ",[15,824,361],{}," hinzu:",[312,827,829],{"className":541,"code":828,"language":543,"meta":317,"style":317},"# In .env, set:\nCONVEX_ADMIN_KEY=\u003Cpaste-key-here>\n",[15,830,831,836],{"__ignoreMap":317},[321,832,833],{"class":323,"line":324},[321,834,835],{"class":372},"# In .env, set:\n",[321,837,838,841,844,847],{"class":323,"line":376},[321,839,840],{"class":379},"CONVEX_ADMIN_KEY",[321,842,843],{"class":339},"=\u003C",[321,845,846],{"class":335},"paste-key-here",[321,848,849],{"class":339},">\n",[11,851,852,853,855,856,859,860,862],{},"Ein Neustart ist nicht nötig — die dauerhaft laufenden Services lesen ",[15,854,840],{}," nicht. Nur der einmalig laufende Container ",[15,857,858],{},"convex-deploy"," (nächster Schritt) und die optionalen Sidecars verwenden ihn, und diese lesen die ",[15,861,361],{}," bei jedem Lauf neu ein.",[11,864,865],{},[45,866,867],{},"6. Functions deployen",[312,869,871],{"className":541,"code":870,"language":543,"meta":317,"style":317},"# Deploy the main Convex functions\ndocker compose --profile deploy run --rm convex-deploy\n",[15,872,873,878],{"__ignoreMap":317},[321,874,875],{"class":323,"line":324},[321,876,877],{"class":372},"# Deploy the main Convex functions\n",[321,879,880,882,884,887,890,893,896],{"class":323,"line":376},[321,881,750],{"class":327},[321,883,753],{"class":335},[321,885,886],{"class":331}," --profile",[321,888,889],{"class":335}," deploy",[321,891,892],{"class":335}," run",[321,894,895],{"class":331}," --rm",[321,897,898],{"class":335}," convex-deploy\n",[11,900,901],{},[45,902,903],{},"7. Convex-Umgebungsvariablen setzen",[11,905,906],{},"Dies sind Variablen auf Anwendungsebene, die von den Serverless-Functions gelesen werden. Setzen Sie sie mit dem Convex-CLI und Ihrem Admin-Key:",[312,908,910],{"className":541,"code":909,"language":543,"meta":317,"style":317},"export CONVEX_URL=http:\u002F\u002Flocalhost:3210\nexport CONVEX_ADMIN_KEY=\u003Cyour-admin-key>\n\nnpx convex env set BETTER_AUTH_SECRET \"$(openssl rand -base64 32)\" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY\nnpx convex env set UNSUBSCRIBE_SECRET \"$(openssl rand -base64 32)\" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY\nnpx convex env set SITE_URL \"http:\u002F\u002Flocalhost:3000\" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY\n# Do NOT set CONVEX_SITE_URL here — it is a Convex BUILT-IN and `convex env set`\n# rejects it (EnvVarNameForbidden). The backend derives it from\n# CONVEX_SITE_ORIGIN on the convex container, which docker-compose interpolates\n# from NUXT_PUBLIC_CONVEX_SITE_URL in your .env.\nnpx convex env set EMAIL_PROVIDER \"mta\" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY\nnpx convex env set MTA_API_URL \"http:\u002F\u002Fmta:3100\" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY\nnpx convex env set MTA_API_KEY \"\u003Cyour-mta-api-key>\" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY\nnpx convex env set MTA_WEBHOOK_SECRET \"\u003Cyour-mta-webhook-secret>\" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY\nnpx convex env set DEFAULT_FROM_EMAIL \"noreply@example.com\" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY\nnpx convex env set DEFAULT_FROM_NAME \"Owlat\" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY\nnpx convex env set DEFAULT_FROM_DOMAIN \"example.com\" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY\n",[15,911,912,925,939,943,988,1021,1045,1050,1055,1061,1067,1092,1117,1142,1167,1192,1217],{"__ignoreMap":317},[321,913,914,917,920,922],{"class":323,"line":324},[321,915,916],{"class":339},"export",[321,918,919],{"class":379}," CONVEX_URL",[321,921,383],{"class":339},[321,923,924],{"class":379},"http:\u002F\u002Flocalhost:3210\n",[321,926,927,929,932,934,937],{"class":323,"line":376},[321,928,916],{"class":339},[321,930,931],{"class":379}," CONVEX_ADMIN_KEY",[321,933,843],{"class":339},[321,935,936],{"class":379},"your-admin-key",[321,938,849],{"class":339},[321,940,941],{"class":323,"line":400},[321,942,404],{"emptyLinePlaceholder":403},[321,944,945,948,950,953,956,959,962,964,967,970,973,976,979,982,985],{"class":323,"line":407},[321,946,947],{"class":327},"npx",[321,949,816],{"class":335},[321,951,952],{"class":335}," env",[321,954,955],{"class":335}," set",[321,957,958],{"class":335}," BETTER_AUTH_SECRET",[321,960,961],{"class":335}," \"$(",[321,963,131],{"class":327},[321,965,966],{"class":335}," rand ",[321,968,969],{"class":331},"-base64",[321,971,972],{"class":331}," 32",[321,974,975],{"class":335},")\"",[321,977,978],{"class":331}," --url",[321,980,981],{"class":379}," $CONVEX_URL ",[321,983,984],{"class":331},"--admin-key",[321,986,987],{"class":379}," $CONVEX_ADMIN_KEY\n",[321,989,990,992,994,996,998,1001,1003,1005,1007,1009,1011,1013,1015,1017,1019],{"class":323,"line":413},[321,991,947],{"class":327},[321,993,816],{"class":335},[321,995,952],{"class":335},[321,997,955],{"class":335},[321,999,1000],{"class":335}," UNSUBSCRIBE_SECRET",[321,1002,961],{"class":335},[321,1004,131],{"class":327},[321,1006,966],{"class":335},[321,1008,969],{"class":331},[321,1010,972],{"class":331},[321,1012,975],{"class":335},[321,1014,978],{"class":331},[321,1016,981],{"class":379},[321,1018,984],{"class":331},[321,1020,987],{"class":379},[321,1022,1023,1025,1027,1029,1031,1034,1037,1039,1041,1043],{"class":323,"line":434},[321,1024,947],{"class":327},[321,1026,816],{"class":335},[321,1028,952],{"class":335},[321,1030,955],{"class":335},[321,1032,1033],{"class":335}," SITE_URL",[321,1035,1036],{"class":335}," \"http:\u002F\u002Flocalhost:3000\"",[321,1038,978],{"class":331},[321,1040,981],{"class":379},[321,1042,984],{"class":331},[321,1044,987],{"class":379},[321,1046,1047],{"class":323,"line":439},[321,1048,1049],{"class":372},"# Do NOT set CONVEX_SITE_URL here — it is a Convex BUILT-IN and `convex env set`\n",[321,1051,1052],{"class":323,"line":445},[321,1053,1054],{"class":372},"# rejects it (EnvVarNameForbidden). The backend derives it from\n",[321,1056,1058],{"class":323,"line":1057},9,[321,1059,1060],{"class":372},"# CONVEX_SITE_ORIGIN on the convex container, which docker-compose interpolates\n",[321,1062,1064],{"class":323,"line":1063},10,[321,1065,1066],{"class":372},"# from NUXT_PUBLIC_CONVEX_SITE_URL in your .env.\n",[321,1068,1070,1072,1074,1076,1078,1081,1084,1086,1088,1090],{"class":323,"line":1069},11,[321,1071,947],{"class":327},[321,1073,816],{"class":335},[321,1075,952],{"class":335},[321,1077,955],{"class":335},[321,1079,1080],{"class":335}," EMAIL_PROVIDER",[321,1082,1083],{"class":335}," \"mta\"",[321,1085,978],{"class":331},[321,1087,981],{"class":379},[321,1089,984],{"class":331},[321,1091,987],{"class":379},[321,1093,1095,1097,1099,1101,1103,1106,1109,1111,1113,1115],{"class":323,"line":1094},12,[321,1096,947],{"class":327},[321,1098,816],{"class":335},[321,1100,952],{"class":335},[321,1102,955],{"class":335},[321,1104,1105],{"class":335}," MTA_API_URL",[321,1107,1108],{"class":335}," \"http:\u002F\u002Fmta:3100\"",[321,1110,978],{"class":331},[321,1112,981],{"class":379},[321,1114,984],{"class":331},[321,1116,987],{"class":379},[321,1118,1120,1122,1124,1126,1128,1131,1134,1136,1138,1140],{"class":323,"line":1119},13,[321,1121,947],{"class":327},[321,1123,816],{"class":335},[321,1125,952],{"class":335},[321,1127,955],{"class":335},[321,1129,1130],{"class":335}," MTA_API_KEY",[321,1132,1133],{"class":335}," \"\u003Cyour-mta-api-key>\"",[321,1135,978],{"class":331},[321,1137,981],{"class":379},[321,1139,984],{"class":331},[321,1141,987],{"class":379},[321,1143,1145,1147,1149,1151,1153,1156,1159,1161,1163,1165],{"class":323,"line":1144},14,[321,1146,947],{"class":327},[321,1148,816],{"class":335},[321,1150,952],{"class":335},[321,1152,955],{"class":335},[321,1154,1155],{"class":335}," MTA_WEBHOOK_SECRET",[321,1157,1158],{"class":335}," \"\u003Cyour-mta-webhook-secret>\"",[321,1160,978],{"class":331},[321,1162,981],{"class":379},[321,1164,984],{"class":331},[321,1166,987],{"class":379},[321,1168,1170,1172,1174,1176,1178,1181,1184,1186,1188,1190],{"class":323,"line":1169},15,[321,1171,947],{"class":327},[321,1173,816],{"class":335},[321,1175,952],{"class":335},[321,1177,955],{"class":335},[321,1179,1180],{"class":335}," DEFAULT_FROM_EMAIL",[321,1182,1183],{"class":335}," \"noreply@example.com\"",[321,1185,978],{"class":331},[321,1187,981],{"class":379},[321,1189,984],{"class":331},[321,1191,987],{"class":379},[321,1193,1195,1197,1199,1201,1203,1206,1209,1211,1213,1215],{"class":323,"line":1194},16,[321,1196,947],{"class":327},[321,1198,816],{"class":335},[321,1200,952],{"class":335},[321,1202,955],{"class":335},[321,1204,1205],{"class":335}," DEFAULT_FROM_NAME",[321,1207,1208],{"class":335}," \"Owlat\"",[321,1210,978],{"class":331},[321,1212,981],{"class":379},[321,1214,984],{"class":331},[321,1216,987],{"class":379},[321,1218,1220,1222,1224,1226,1228,1231,1234,1236,1238,1240],{"class":323,"line":1219},17,[321,1221,947],{"class":327},[321,1223,816],{"class":335},[321,1225,952],{"class":335},[321,1227,955],{"class":335},[321,1229,1230],{"class":335}," DEFAULT_FROM_DOMAIN",[321,1232,1233],{"class":335}," \"example.com\"",[321,1235,978],{"class":331},[321,1237,981],{"class":379},[321,1239,984],{"class":331},[321,1241,987],{"class":379},[465,1243,1245],{"title":1244,"type":468},"Wie der Assistent es macht",[11,1246,1247,1248,1251,1252,1255,1256,1258,1259,1262,1263,1266,1267,1269],{},"Die obigen ",[15,1249,1250],{},"npx convex env set","-Befehle auf dem Host sind ein manuelles Äquivalent. Das unterstützte Tooling (",[15,1253,1254],{},"apps\u002Fsetup-cli\u002Fsrc\u002Flib\u002FconvexDeploy.ts",") überträgt diese Variablen stattdessen über den Container ",[15,1257,858],{}," — ",[15,1260,1261],{},"docker compose --profile deploy run --rm convex-deploy"," —, der das gepinnte Convex-CLI und ",[15,1264,1265],{},"CONVEX_SELF_HOSTED_URL=http:\u002F\u002Fconvex:3210"," bereits mitbringt. Convex-Functions lesen diese Werte aus dem Deployment, nicht aus der ",[15,1268,361],{}," von Compose.",[11,1271,1272],{},[45,1273,1274],{},"8. Dashboard öffnen",[312,1276,1278],{"className":541,"code":1277,"language":543,"meta":317,"style":317},"open http:\u002F\u002Flocalhost:3000\n",[15,1279,1280],{"__ignoreMap":317},[321,1281,1282,1285],{"class":323,"line":324},[321,1283,1284],{"class":327},"open",[321,1286,1287],{"class":335}," http:\u002F\u002Flocalhost:3000\n",[465,1289,1291],{"title":1290,"type":468},"Admin-Konto",[11,1292,1293,1294,1297,1298,1301,1302,1305,1306,1309],{},"Der Setup-Assistent legt automatisch ein Admin-Konto an. Beim manuellen Setup starten Sie zunächst das profilgesteuerte Dashboard mit ",[15,1295,1296],{},"docker compose --profile dashboard up -d convex-dashboard"," (es bindet nur an ",[15,1299,1300],{},"127.0.0.1"," — bei entfernten Hosts tunneln Sie mit ",[15,1303,1304],{},"ssh -L 6791:127.0.0.1:6791 host",") und öffnen dann ",[15,1307,1308],{},"http:\u002F\u002Flocalhost:6791",", um Ihren ersten Benutzer anzulegen; alternativ rufen Sie den im Setup-Skript dokumentierten Seed-Endpunkt auf.",[34,1311,31],{"id":1312},"stack-überblick",[72,1314,1315,1327],{},[75,1316,1317],{},[78,1318,1319,1322,1325],{},[81,1320,1321],{},"Service",[81,1323,1324],{},"Port(s)",[81,1326,158],{},[88,1328,1329,1342,1355,1383,1396,1420,1456],{},[78,1330,1331,1336,1339],{},[93,1332,1333],{},[45,1334,1335],{},"Convex",[93,1337,1338],{},"3210, 3211",[93,1340,1341],{},"Datenbank, Echtzeit-Subscriptions, Serverless-Functions, Dateispeicher",[78,1343,1344,1349,1352],{},[93,1345,1346],{},[45,1347,1348],{},"Web",[93,1350,1351],{},"3000",[93,1353,1354],{},"Nuxt-Anwendung — Dashboard, E-Mail-Builder, Einstellungen",[78,1356,1357,1362,1365],{},[93,1358,1359],{},[45,1360,1361],{},"MTA",[93,1363,1364],{},"3100, 25",[93,1366,1367,1368,1371,1372,1374,1375,1378,1379,1382],{},"Mail Transfer Agent — SMTP-Zustellung, Bounce-Verarbeitung, IP-Warming. ",[45,1369,1370],{},"Profilgesteuert"," (",[15,1373,17],{},"); die Standarddatei ",[15,1376,1377],{},".env.selfhost.example"," liefert ",[15,1380,1381],{},"COMPOSE_PROFILES=mta"," aus, der Dienst läuft also bei einem Standard-Self-Host, wird aber übersprungen, wenn Sie auf einen Drittanbieter (Resend\u002FSES) umstellen",[78,1384,1385,1390,1393],{},[93,1386,1387],{},[45,1388,1389],{},"Redis",[93,1391,1392],{},"nur intern (6379)",[93,1394,1395],{},"Job-Queue und Rate-Limiting-Zustand für den MTA",[78,1397,1398,1403,1406],{},[93,1399,1400],{},[45,1401,1402],{},"ClamAV",[93,1404,1405],{},"nur intern (3310)",[93,1407,1408,1409,1371,1411,1413,1414,1416,1417,1419],{},"Virenprüfung für E-Mail-Anhänge. ",[45,1410,1370],{},[15,1412,21],{},") und ",[45,1415,269],{}," in den Standard-",[15,1418,25],{}," enthalten — siehe Hinweis unten",[78,1421,1422,1427,1430],{},[93,1423,1424],{},[45,1425,1426],{},"Updater",[93,1428,1429],{},"nur intern (3200)",[93,1431,1432,1433,1436,1437,1440,1441,1444,1445,1448,1449,1452,1453],{},"Sidecar für Selbst-Updates aus der App heraus — führt stellvertretend für die Web-App ",[15,1434,1435],{},"docker compose pull && up -d"," aus. Bindet ausschließlich ",[15,1438,1439],{},"OWLAT_INSTALL_DIR"," ein; der Docker-Socket wird nicht mehr eingebunden, stattdessen wird Docker über den Least-Privilege-",[15,1442,1443],{},"docker-socket-proxy"," gesteuert (",[15,1446,1447],{},"DOCKER_HOST=tcp:\u002F\u002Fdocker-socket-proxy:2375","), den einzigen Container, der ",[15,1450,1451],{},"\u002Fvar\u002Frun\u002Fdocker.sock"," einbindet (schreibgeschützt). Erreichbar über das Docker-Netzwerk unter ",[15,1454,1455],{},"http:\u002F\u002Fupdater:3200",[78,1457,1458,1463,1466],{},[93,1459,1460],{},[45,1461,1462],{},"Caddy",[93,1464,1465],{},"80, 443",[93,1467,1468,1469,1472],{},"Optionaler Reverse Proxy mit automatischem HTTPS, aktivierbar über ",[15,1470,1471],{},"--profile tls"," (siehe Caddyfile.example)",[465,1474,1477],{"title":1475,"type":1476},"`docker compose up -d` startet nur die Profile in COMPOSE_PROFILES","warning",[11,1478,1479,1481,1482,1484,1485,1487,1488,1491,1492,1378,1494,1496,1497,1499,1500,1503,1504,1507,1508,1511,1512,1514,1515,1517,1518,1521,1522,1524,1525,1527,1528,1530],{},[15,1480,509],{}," berücksichtigt ",[15,1483,25],{}," aus Ihrer ",[15,1486,361],{},": Es startet die dauerhaft laufenden Basis-Services plus die dort aufgeführten Profile und ",[45,1489,1490],{},"überspringt jeden profilgesteuerten Service, der nicht aufgeführt ist",". Die Standarddatei ",[15,1493,1377],{},[15,1495,1381],{}," aus — damit startet der MTA, aber ",[45,1498,269],{}," ClamAV. Auch wenn das Feature-Flag ",[15,1501,1502],{},"scan.files"," standardmäßig aktiv ist und die Anhangsprüfung ",[45,1505,1506],{},"fail-open"," arbeitet (Mail wird auch zugestellt, wenn der Scanner nicht erreichbar ist), startet ClamAV bei einer Standardinstallation nie — ",[45,1509,1510],{},"Anhänge werden also ungeprüft zugestellt",". Damit Dateien tatsächlich geprüft werden, ergänzen Sie ",[15,1513,21],{}," in ",[15,1516,25],{}," (z. B. ",[15,1519,1520],{},"COMPOSE_PROFILES=mta,clamav",") und führen ",[15,1523,509],{}," erneut aus, oder aktivieren Sie das Paket ",[15,1526,1502],{}," über den Assistenten, der das Profil ",[15,1529,21],{}," für Sie einbindet.",[11,1532,1533],{},"Einige weitere Services laufen nur bei Bedarf und nicht als Teil des dauerhaft laufenden Stacks:",[39,1535,1536,1552,1567],{},[42,1537,1538,1371,1542,1545,1546,1549,1550,32],{},[45,1539,1540],{},[15,1541,858],{},[15,1543,1544],{},"--profile deploy",") — einmalig laufender Container, der die Functions, das Schema und die HTTP-Routen aus ",[15,1547,1548],{},"apps\u002Fapi"," 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: ",[15,1551,1261],{},[42,1553,1554,1371,1559,1562,1563,1566],{},[45,1555,1556],{},[15,1557,1558],{},"code-worker",[15,1560,1561],{},"--profile inbox-codetasks",", ebenfalls über ",[15,1564,1565],{},"--profile dev"," verfügbar) — der optionale Sidecar für den KI-Coding-Agenten; standardmäßig aus.",[42,1568,1569,1371,1574,1577,1578,1580,1581,1583,1584,1586,1587,1589],{},[45,1570,1571],{},[15,1572,1573],{},"convex-dashboard",[15,1575,1576],{},"--profile dashboard",") — Admin-Oberfläche zum Inspizieren von Daten und Debuggen von Functions. Starten Sie sie mit ",[15,1579,1296],{},". Sie bindet nur an ",[15,1582,1300],{},"; öffnen Sie auf einem entfernten VPS daher einen SSH-Tunnel (",[15,1585,1304],{},"), bevor Sie ",[15,1588,1308],{}," aufrufen.",[11,1591,1592,1593,32],{},"Einen tieferen Einblick in die Architektur finden Sie unter ",[28,1594,1596],{"href":1595},"\u002Fvision\u002Fself-hosting","Self-Hosting-Architektur",[34,1598,1600],{"id":1599},"wie-geht-es-weiter","Wie geht es weiter",[39,1602,1603,1610,1617,1624],{},[42,1604,1605,1609],{},[28,1606,1608],{"href":1607},"\u002Fdeveloper\u002Fself-hosting-config","Konfigurationsreferenz"," — alle Umgebungsvariablen, Service-Topologie und Volumes",[42,1611,1612,1616],{},[28,1613,1615],{"href":1614},"\u002Fdeveloper\u002Fself-hosting-dns-email","DNS- & E-Mail-Setup"," — SPF, DKIM, DMARC und Bounce-Verarbeitung für den produktiven E-Mail-Versand",[42,1618,1619,1623],{},[28,1620,1622],{"href":1621},"\u002Fdeveloper\u002Fself-hosting-production","Produktions-Deployment"," — Reverse Proxy, TLS, Firewall, Backups und Monitoring",[42,1625,1626,1630],{},[28,1627,1629],{"href":1628},"\u002Fdeveloper\u002Fself-hosting-maintenance","Wartung & Updates"," — Ihre Instanz aktuell halten und Fehler beheben",[1632,1633,1634],"style",{},"html pre.shiki code .sOLd2, html code.shiki .sOLd2{--shiki-default:#6F42C1;--shiki-dark:#F69D50}html pre.shiki code .sviXB, html code.shiki .sviXB{--shiki-default:#005CC5;--shiki-dark:#6CB6FF}html pre.shiki code .s-HuK, html code.shiki .s-HuK{--shiki-default:#032F62;--shiki-dark:#96D0FF}html pre.shiki code .s7YZ4, html code.shiki .s7YZ4{--shiki-default:#D73A49;--shiki-dark:#F47067}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sDN9O, html code.shiki .sDN9O{--shiki-default:#6A737D;--shiki-dark:#768390}html pre.shiki code .sYgZi, html code.shiki .sYgZi{--shiki-default:#24292E;--shiki-dark:#ADBAC7}",{"title":317,"searchDepth":376,"depth":376,"links":1636},[1637,1638,1639,1644,1645,1650,1651],{"id":36,"depth":376,"text":37},{"id":69,"depth":376,"text":70},{"id":145,"depth":376,"text":146,"children":1640},[1641,1642,1643],{"id":216,"depth":400,"text":176},{"id":251,"depth":400,"text":191},{"id":287,"depth":400,"text":205},{"id":306,"depth":376,"text":307},{"id":512,"depth":376,"text":513,"children":1646},[1647,1649],{"id":516,"depth":400,"text":1648},"Option A: owlat quickstart (empfohlen)",{"id":625,"depth":400,"text":626},{"id":1312,"depth":376,"text":31},{"id":1599,"depth":376,"text":1600},"Owlat mit Docker Compose auf Ihrer eigenen Infrastruktur betreiben. Vollständige Anleitung vom ersten Start bis zur Produktion.","md",{},"\u002Fdeveloper\u002Fself-hosting",{"title":6,"description":1652},"3.developer\u002F30.self-hosting","HNKqCVuxJfpdDdprTZwSzsLHjz80oOfYgJZcZCoJtZU",[1660,1664],{"title":1661,"path":1662,"stem":1663,"children":-1},"Scopes","\u002Fdeveloper\u002Fscopes","3.developer\u002F3.scopes",{"title":1665,"path":1607,"stem":1666,"children":-1},"Self-Hosting-Konfiguration","3.developer\u002F31.self-hosting-config",1786915106501]