[{"data":1,"prerenderedAt":1188},["ShallowReactive",2],{"content-developer\u002Fself-hosting":3,"surround-\u002Fdeveloper\u002Fself-hosting":1180},{"id":4,"title":5,"body":6,"description":1173,"extension":1174,"meta":1175,"navigation":207,"path":1176,"seo":1177,"stem":1178,"__hash__":1179},"content\u002F3.developer\u002F30.self-hosting.md","Self-Hosting",{"type":7,"value":8,"toc":1162},"minimark",[9,13,18,47,51,124,128,131,164,167,236,264,268,273,280,318,321,325,330,360,365,419,437,442,461,466,496,501,521,527,551,554,568,573,630,635,638,976,981,994,1004,1008,1116,1124,1128,1158],[10,11,12],"p",{},"Owlat runs entirely on your own infrastructure via a single Docker Compose file. No cloud services are required — the stack includes its own database, real-time engine, mail server, and antivirus scanner.",[14,15,17],"h2",{"id":16},"why-self-host","Why Self-Host",[19,20,21,29,35,41],"ul",{},[22,23,24,28],"li",{},[25,26,27],"strong",{},"Data sovereignty"," — your emails, contacts, and analytics never leave your servers",[22,30,31,34],{},[25,32,33],{},"Compliance"," — meet GDPR, HIPAA, or industry-specific data residency requirements",[22,36,37,40],{},[25,38,39],{},"Air-gapped environments"," — run without any external network dependencies",[22,42,43,46],{},[25,44,45],{},"Full control"," — customize, audit, and extend every component",[14,48,50],{"id":49},"prerequisites","Prerequisites",[52,53,54,67],"table",{},[55,56,57],"thead",{},[58,59,60,64],"tr",{},[61,62,63],"th",{},"Requirement",[61,65,66],{},"Minimum",[68,69,70,79,92,100,108,116],"tbody",{},[58,71,72,76],{},[73,74,75],"td",{},"Docker",[73,77,78],{},"20.10+",[58,80,81,84],{},[73,82,83],{},"Docker Compose",[73,85,86,87,91],{},"v2 (the ",[88,89,90],"code",{},"docker compose"," plugin)",[58,93,94,97],{},[73,95,96],{},"RAM",[73,98,99],{},"4 GB (8 GB recommended)",[58,101,102,105],{},[73,103,104],{},"Disk",[73,106,107],{},"20 GB free",[58,109,110,113],{},[73,111,112],{},"openssl",[73,114,115],{},"Any version (for generating secrets)",[58,117,118,121],{},[73,119,120],{},"Domain + DNS control",[73,122,123],{},"Required for production email delivery",[14,125,127],{"id":126},"fastest-path","Fastest path",[10,129,130],{},"On a fresh Linux VPS with Docker + Docker Compose v2 installed:",[132,133,138],"pre",{"className":134,"code":135,"language":136,"meta":137,"style":137},"language-sh shiki shiki-themes github-light github-dark-dimmed","curl -fsSL https:\u002F\u002Fget.owlat.app | bash\n","sh","",[88,139,140],{"__ignoreMap":137},[141,142,145,149,153,157,161],"span",{"class":143,"line":144},"line",1,[141,146,148],{"class":147},"sOLd2","curl",[141,150,152],{"class":151},"sviXB"," -fsSL",[141,154,156],{"class":155},"s-HuK"," https:\u002F\u002Fget.owlat.app",[141,158,160],{"class":159},"s7YZ4"," |",[141,162,163],{"class":147}," bash\n",[10,165,166],{},"The one-liner clones the repo, runs preflight checks, and hands off to the interactive setup wizard described below. Non-interactive usage:",[132,168,170],{"className":134,"code":169,"language":136,"meta":137,"style":137},"# 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",[88,171,172,178,202,209,215],{"__ignoreMap":137},[141,173,174],{"class":143,"line":144},[141,175,177],{"class":176},"sDN9O","# Skip prompts, accept all defaults\n",[141,179,181,185,188,191,194,196,198,200],{"class":143,"line":180},2,[141,182,184],{"class":183},"sYgZi","OWLAT_ASSUME_YES",[141,186,187],{"class":159},"=",[141,189,190],{"class":155},"1",[141,192,193],{"class":147}," curl",[141,195,152],{"class":151},[141,197,156],{"class":155},[141,199,160],{"class":159},[141,201,163],{"class":147},[141,203,205],{"class":143,"line":204},3,[141,206,208],{"emptyLinePlaceholder":207},true,"\n",[141,210,212],{"class":143,"line":211},4,[141,213,214],{"class":176},"# Read answers from a config file (for CI \u002F Ansible)\n",[141,216,218,221,223,226,228,230,232,234],{"class":143,"line":217},5,[141,219,220],{"class":183},"OWLAT_CONFIG_FILE",[141,222,187],{"class":159},[141,224,225],{"class":155},"\u002Fpath\u002Fto\u002Fanswers.env",[141,227,193],{"class":147},[141,229,152],{"class":151},[141,231,156],{"class":155},[141,233,160],{"class":159},[141,235,163],{"class":147},[237,238,241],"callout",{"title":239,"type":240},"Not included in self-hosted","info",[10,242,243,244,247,248,251,252,255,256,259,260,263],{},"Stripe billing, Hetzner VPS provisioning, and tier management live in the hosted-cloud ",[25,245,246],{},"control plane"," (",[88,249,250],{},"apps\u002Fnest"," + ",[88,253,254],{},"apps\u002Fnest-api",") and are disabled in self-hosted mode. Run with ",[88,257,258],{},"docker compose up -d"," — the control plane is gated behind ",[88,261,262],{},"--profile hosted"," and only operators of the managed service need it.",[14,265,267],{"id":266},"quick-start","Quick Start",[269,270,272],"h3",{"id":271},"option-a-setup-wizard-recommended","Option A: Setup Wizard (recommended)",[10,274,275,276,279],{},"The interactive wizard generates secrets, writes your ",[88,277,278],{},".env",", starts the Docker stack, deploys functions, and creates your admin account — all automatically.",[132,281,285],{"className":282,"code":283,"language":284,"meta":137,"style":137},"language-bash shiki shiki-themes github-light github-dark-dimmed","git clone https:\u002F\u002Fgithub.com\u002Fowlat\u002Fowlat.git\ncd owlat\nbash scripts\u002Fsetup.sh\n# Select \"Self-Hosted (Docker Compose)\" when prompted\n","bash",[88,286,287,298,306,313],{"__ignoreMap":137},[141,288,289,292,295],{"class":143,"line":144},[141,290,291],{"class":147},"git",[141,293,294],{"class":155}," clone",[141,296,297],{"class":155}," https:\u002F\u002Fgithub.com\u002Fowlat\u002Fowlat.git\n",[141,299,300,303],{"class":143,"line":180},[141,301,302],{"class":151},"cd",[141,304,305],{"class":155}," owlat\n",[141,307,308,310],{"class":143,"line":204},[141,309,284],{"class":147},[141,311,312],{"class":155}," scripts\u002Fsetup.sh\n",[141,314,315],{"class":143,"line":211},[141,316,317],{"class":176},"# Select \"Self-Hosted (Docker Compose)\" when prompted\n",[10,319,320],{},"The wizard takes about 5 minutes and handles everything below.",[269,322,324],{"id":323},"option-b-manual-setup","Option B: Manual Setup",[10,326,327],{},[25,328,329],{},"1. Clone and configure",[132,331,333],{"className":282,"code":332,"language":284,"meta":137,"style":137},"git clone https:\u002F\u002Fgithub.com\u002Fowlat\u002Fowlat.git\ncd owlat\ncp .env.selfhost.example .env\n",[88,334,335,343,349],{"__ignoreMap":137},[141,336,337,339,341],{"class":143,"line":144},[141,338,291],{"class":147},[141,340,294],{"class":155},[141,342,297],{"class":155},[141,344,345,347],{"class":143,"line":180},[141,346,302],{"class":151},[141,348,305],{"class":155},[141,350,351,354,357],{"class":143,"line":204},[141,352,353],{"class":147},"cp",[141,355,356],{"class":155}," .env.selfhost.example",[141,358,359],{"class":155}," .env\n",[10,361,362],{},[25,363,364],{},"2. Generate secrets",[132,366,368],{"className":282,"code":367,"language":284,"meta":137,"style":137},"# 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",[88,369,370,375,388,392,397,408],{"__ignoreMap":137},[141,371,372],{"class":143,"line":144},[141,373,374],{"class":176},"# Instance secret (hex)\n",[141,376,377,379,382,385],{"class":143,"line":180},[141,378,112],{"class":147},[141,380,381],{"class":155}," rand",[141,383,384],{"class":151}," -hex",[141,386,387],{"class":151}," 32\n",[141,389,390],{"class":143,"line":204},[141,391,208],{"emptyLinePlaceholder":207},[141,393,394],{"class":143,"line":211},[141,395,396],{"class":176},"# MTA API key and webhook secret (base64)\n",[141,398,399,401,403,406],{"class":143,"line":217},[141,400,112],{"class":147},[141,402,381],{"class":155},[141,404,405],{"class":151}," -base64",[141,407,387],{"class":151},[141,409,411,413,415,417],{"class":143,"line":410},6,[141,412,112],{"class":147},[141,414,381],{"class":155},[141,416,405],{"class":151},[141,418,387],{"class":151},[10,420,421,422,424,425,428,429,432,433,436],{},"Paste the generated values into ",[88,423,278],{}," for ",[88,426,427],{},"INSTANCE_SECRET",", ",[88,430,431],{},"MTA_API_KEY",", and ",[88,434,435],{},"MTA_WEBHOOK_SECRET",".",[10,438,439],{},[25,440,441],{},"3. Start the stack",[132,443,445],{"className":282,"code":444,"language":284,"meta":137,"style":137},"docker compose up -d\n",[88,446,447],{"__ignoreMap":137},[141,448,449,452,455,458],{"class":143,"line":144},[141,450,451],{"class":147},"docker",[141,453,454],{"class":155}," compose",[141,456,457],{"class":155}," up",[141,459,460],{"class":151}," -d\n",[10,462,463],{},[25,464,465],{},"4. Wait for Convex to be ready",[132,467,469],{"className":282,"code":468,"language":284,"meta":137,"style":137},"# Watch logs until you see \"ready\" or \"listening\"\ndocker compose logs -f convex\n# Press Ctrl+C once ready\n",[88,470,471,476,491],{"__ignoreMap":137},[141,472,473],{"class":143,"line":144},[141,474,475],{"class":176},"# Watch logs until you see \"ready\" or \"listening\"\n",[141,477,478,480,482,485,488],{"class":143,"line":180},[141,479,451],{"class":147},[141,481,454],{"class":155},[141,483,484],{"class":155}," logs",[141,486,487],{"class":151}," -f",[141,489,490],{"class":155}," convex\n",[141,492,493],{"class":143,"line":204},[141,494,495],{"class":176},"# Press Ctrl+C once ready\n",[10,497,498],{},[25,499,500],{},"5. Generate the admin key",[132,502,504],{"className":282,"code":503,"language":284,"meta":137,"style":137},"docker compose exec convex .\u002Fgenerate_admin_key.sh\n",[88,505,506],{"__ignoreMap":137},[141,507,508,510,512,515,518],{"class":143,"line":144},[141,509,451],{"class":147},[141,511,454],{"class":155},[141,513,514],{"class":155}," exec",[141,516,517],{"class":155}," convex",[141,519,520],{"class":155}," .\u002Fgenerate_admin_key.sh\n",[10,522,523,524,526],{},"Copy the output key and add it to your ",[88,525,278],{},":",[132,528,530],{"className":282,"code":529,"language":284,"meta":137,"style":137},"# In .env, set:\nCONVEX_ADMIN_KEY=\u003Cpaste-key-here>\n",[88,531,532,537],{"__ignoreMap":137},[141,533,534],{"class":143,"line":144},[141,535,536],{"class":176},"# In .env, set:\n",[141,538,539,542,545,548],{"class":143,"line":180},[141,540,541],{"class":183},"CONVEX_ADMIN_KEY",[141,543,544],{"class":159},"=\u003C",[141,546,547],{"class":155},"paste-key-here",[141,549,550],{"class":159},">\n",[10,552,553],{},"Restart to pick up the new key:",[132,555,556],{"className":282,"code":444,"language":284,"meta":137,"style":137},[88,557,558],{"__ignoreMap":137},[141,559,560,562,564,566],{"class":143,"line":144},[141,561,451],{"class":147},[141,563,454],{"class":155},[141,565,457],{"class":155},[141,567,460],{"class":151},[10,569,570],{},[25,571,572],{},"6. Deploy functions",[132,574,576],{"className":282,"code":575,"language":284,"meta":137,"style":137},"# Deploy the main Convex functions\ndocker compose --profile deploy run --rm convex-deploy\n\n# Deploy the control plane functions\ndocker compose --profile deploy run --rm nest-api-deploy\n",[88,577,578,583,604,608,613],{"__ignoreMap":137},[141,579,580],{"class":143,"line":144},[141,581,582],{"class":176},"# Deploy the main Convex functions\n",[141,584,585,587,589,592,595,598,601],{"class":143,"line":180},[141,586,451],{"class":147},[141,588,454],{"class":155},[141,590,591],{"class":151}," --profile",[141,593,594],{"class":155}," deploy",[141,596,597],{"class":155}," run",[141,599,600],{"class":151}," --rm",[141,602,603],{"class":155}," convex-deploy\n",[141,605,606],{"class":143,"line":204},[141,607,208],{"emptyLinePlaceholder":207},[141,609,610],{"class":143,"line":211},[141,611,612],{"class":176},"# Deploy the control plane functions\n",[141,614,615,617,619,621,623,625,627],{"class":143,"line":217},[141,616,451],{"class":147},[141,618,454],{"class":155},[141,620,591],{"class":151},[141,622,594],{"class":155},[141,624,597],{"class":155},[141,626,600],{"class":151},[141,628,629],{"class":155}," nest-api-deploy\n",[10,631,632],{},[25,633,634],{},"7. Set Convex environment variables",[10,636,637],{},"These are application-level variables read by the serverless functions. Set them using the Convex CLI with your admin key:",[132,639,641],{"className":282,"code":640,"language":284,"meta":137,"style":137},"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\nnpx convex env set CONVEX_SITE_URL \"http:\u002F\u002Flocalhost:3211\" --url $CONVEX_URL --admin-key $CONVEX_ADMIN_KEY\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",[88,642,643,656,670,674,719,752,776,801,826,851,876,901,926,951],{"__ignoreMap":137},[141,644,645,648,651,653],{"class":143,"line":144},[141,646,647],{"class":159},"export",[141,649,650],{"class":183}," CONVEX_URL",[141,652,187],{"class":159},[141,654,655],{"class":183},"http:\u002F\u002Flocalhost:3210\n",[141,657,658,660,663,665,668],{"class":143,"line":180},[141,659,647],{"class":159},[141,661,662],{"class":183}," CONVEX_ADMIN_KEY",[141,664,544],{"class":159},[141,666,667],{"class":183},"your-admin-key",[141,669,550],{"class":159},[141,671,672],{"class":143,"line":204},[141,673,208],{"emptyLinePlaceholder":207},[141,675,676,679,681,684,687,690,693,695,698,701,704,707,710,713,716],{"class":143,"line":211},[141,677,678],{"class":147},"npx",[141,680,517],{"class":155},[141,682,683],{"class":155}," env",[141,685,686],{"class":155}," set",[141,688,689],{"class":155}," BETTER_AUTH_SECRET",[141,691,692],{"class":155}," \"$(",[141,694,112],{"class":147},[141,696,697],{"class":155}," rand ",[141,699,700],{"class":151},"-base64",[141,702,703],{"class":151}," 32",[141,705,706],{"class":155},")\"",[141,708,709],{"class":151}," --url",[141,711,712],{"class":183}," $CONVEX_URL ",[141,714,715],{"class":151},"--admin-key",[141,717,718],{"class":183}," $CONVEX_ADMIN_KEY\n",[141,720,721,723,725,727,729,732,734,736,738,740,742,744,746,748,750],{"class":143,"line":217},[141,722,678],{"class":147},[141,724,517],{"class":155},[141,726,683],{"class":155},[141,728,686],{"class":155},[141,730,731],{"class":155}," UNSUBSCRIBE_SECRET",[141,733,692],{"class":155},[141,735,112],{"class":147},[141,737,697],{"class":155},[141,739,700],{"class":151},[141,741,703],{"class":151},[141,743,706],{"class":155},[141,745,709],{"class":151},[141,747,712],{"class":183},[141,749,715],{"class":151},[141,751,718],{"class":183},[141,753,754,756,758,760,762,765,768,770,772,774],{"class":143,"line":410},[141,755,678],{"class":147},[141,757,517],{"class":155},[141,759,683],{"class":155},[141,761,686],{"class":155},[141,763,764],{"class":155}," SITE_URL",[141,766,767],{"class":155}," \"http:\u002F\u002Flocalhost:3000\"",[141,769,709],{"class":151},[141,771,712],{"class":183},[141,773,715],{"class":151},[141,775,718],{"class":183},[141,777,779,781,783,785,787,790,793,795,797,799],{"class":143,"line":778},7,[141,780,678],{"class":147},[141,782,517],{"class":155},[141,784,683],{"class":155},[141,786,686],{"class":155},[141,788,789],{"class":155}," CONVEX_SITE_URL",[141,791,792],{"class":155}," \"http:\u002F\u002Flocalhost:3211\"",[141,794,709],{"class":151},[141,796,712],{"class":183},[141,798,715],{"class":151},[141,800,718],{"class":183},[141,802,804,806,808,810,812,815,818,820,822,824],{"class":143,"line":803},8,[141,805,678],{"class":147},[141,807,517],{"class":155},[141,809,683],{"class":155},[141,811,686],{"class":155},[141,813,814],{"class":155}," EMAIL_PROVIDER",[141,816,817],{"class":155}," \"mta\"",[141,819,709],{"class":151},[141,821,712],{"class":183},[141,823,715],{"class":151},[141,825,718],{"class":183},[141,827,829,831,833,835,837,840,843,845,847,849],{"class":143,"line":828},9,[141,830,678],{"class":147},[141,832,517],{"class":155},[141,834,683],{"class":155},[141,836,686],{"class":155},[141,838,839],{"class":155}," MTA_API_URL",[141,841,842],{"class":155}," \"http:\u002F\u002Fmta:3100\"",[141,844,709],{"class":151},[141,846,712],{"class":183},[141,848,715],{"class":151},[141,850,718],{"class":183},[141,852,854,856,858,860,862,865,868,870,872,874],{"class":143,"line":853},10,[141,855,678],{"class":147},[141,857,517],{"class":155},[141,859,683],{"class":155},[141,861,686],{"class":155},[141,863,864],{"class":155}," MTA_API_KEY",[141,866,867],{"class":155}," \"\u003Cyour-mta-api-key>\"",[141,869,709],{"class":151},[141,871,712],{"class":183},[141,873,715],{"class":151},[141,875,718],{"class":183},[141,877,879,881,883,885,887,890,893,895,897,899],{"class":143,"line":878},11,[141,880,678],{"class":147},[141,882,517],{"class":155},[141,884,683],{"class":155},[141,886,686],{"class":155},[141,888,889],{"class":155}," MTA_WEBHOOK_SECRET",[141,891,892],{"class":155}," \"\u003Cyour-mta-webhook-secret>\"",[141,894,709],{"class":151},[141,896,712],{"class":183},[141,898,715],{"class":151},[141,900,718],{"class":183},[141,902,904,906,908,910,912,915,918,920,922,924],{"class":143,"line":903},12,[141,905,678],{"class":147},[141,907,517],{"class":155},[141,909,683],{"class":155},[141,911,686],{"class":155},[141,913,914],{"class":155}," DEFAULT_FROM_EMAIL",[141,916,917],{"class":155}," \"noreply@example.com\"",[141,919,709],{"class":151},[141,921,712],{"class":183},[141,923,715],{"class":151},[141,925,718],{"class":183},[141,927,929,931,933,935,937,940,943,945,947,949],{"class":143,"line":928},13,[141,930,678],{"class":147},[141,932,517],{"class":155},[141,934,683],{"class":155},[141,936,686],{"class":155},[141,938,939],{"class":155}," DEFAULT_FROM_NAME",[141,941,942],{"class":155}," \"Owlat\"",[141,944,709],{"class":151},[141,946,712],{"class":183},[141,948,715],{"class":151},[141,950,718],{"class":183},[141,952,954,956,958,960,962,965,968,970,972,974],{"class":143,"line":953},14,[141,955,678],{"class":147},[141,957,517],{"class":155},[141,959,683],{"class":155},[141,961,686],{"class":155},[141,963,964],{"class":155}," DEFAULT_FROM_DOMAIN",[141,966,967],{"class":155}," \"example.com\"",[141,969,709],{"class":151},[141,971,712],{"class":183},[141,973,715],{"class":151},[141,975,718],{"class":183},[10,977,978],{},[25,979,980],{},"8. Open the dashboard",[132,982,984],{"className":282,"code":983,"language":284,"meta":137,"style":137},"open http:\u002F\u002Flocalhost:3000\n",[88,985,986],{"__ignoreMap":137},[141,987,988,991],{"class":143,"line":144},[141,989,990],{"class":147},"open",[141,992,993],{"class":155}," http:\u002F\u002Flocalhost:3000\n",[237,995,997],{"title":996,"type":240},"Admin account",[10,998,999,1000,1003],{},"The setup wizard creates an admin account automatically. For manual setup, use the Convex dashboard at ",[88,1001,1002],{},"http:\u002F\u002Flocalhost:6791"," to create your first user, or call the seed endpoint documented in the setup script.",[14,1005,1007],{"id":1006},"stack-overview","Stack Overview",[52,1009,1010,1023],{},[55,1011,1012],{},[58,1013,1014,1017,1020],{},[61,1015,1016],{},"Service",[61,1018,1019],{},"Port(s)",[61,1021,1022],{},"Role",[68,1024,1025,1038,1051,1064,1077,1090,1103],{},[58,1026,1027,1032,1035],{},[73,1028,1029],{},[25,1030,1031],{},"Convex",[73,1033,1034],{},"3210, 3211",[73,1036,1037],{},"Database, real-time subscriptions, serverless functions, file storage",[58,1039,1040,1045,1048],{},[73,1041,1042],{},[25,1043,1044],{},"Convex Dashboard",[73,1046,1047],{},"6791",[73,1049,1050],{},"Admin UI for inspecting data and debugging functions",[58,1052,1053,1058,1061],{},[73,1054,1055],{},[25,1056,1057],{},"Web",[73,1059,1060],{},"3000",[73,1062,1063],{},"Nuxt application — dashboard, email builder, settings",[58,1065,1066,1071,1074],{},[73,1067,1068],{},[25,1069,1070],{},"MTA",[73,1072,1073],{},"3100, 25",[73,1075,1076],{},"Mail transfer agent — SMTP delivery, bounce processing, IP warming",[58,1078,1079,1084,1087],{},[73,1080,1081],{},[25,1082,1083],{},"Redis",[73,1085,1086],{},"6379",[73,1088,1089],{},"Job queue and rate limiting state for the MTA",[58,1091,1092,1097,1100],{},[73,1093,1094],{},[25,1095,1096],{},"ClamAV",[73,1098,1099],{},"3310",[73,1101,1102],{},"Antivirus scanning for email attachments",[58,1104,1105,1110,1113],{},[73,1106,1107],{},[25,1108,1109],{},"Nest",[73,1111,1112],{},"3001",[73,1114,1115],{},"Admin panel for organization and infrastructure management",[10,1117,1118,1119,436],{},"For a deeper dive into the architecture, see ",[1120,1121,1123],"a",{"href":1122},"\u002Fvision\u002Fself-hosting","Self-Hosting Architecture",[14,1125,1127],{"id":1126},"whats-next","What's Next",[19,1129,1130,1137,1144,1151],{},[22,1131,1132,1136],{},[1120,1133,1135],{"href":1134},"\u002Fdeveloper\u002Fself-hosting-config","Configuration Reference"," — all environment variables, service topology, and volumes",[22,1138,1139,1143],{},[1120,1140,1142],{"href":1141},"\u002Fdeveloper\u002Fself-hosting-dns-email","DNS & Email Setup"," — SPF, DKIM, DMARC, and bounce handling for production email",[22,1145,1146,1150],{},[1120,1147,1149],{"href":1148},"\u002Fdeveloper\u002Fself-hosting-production","Production Deployment"," — reverse proxy, TLS, firewall, backups, and monitoring",[22,1152,1153,1157],{},[1120,1154,1156],{"href":1155},"\u002Fdeveloper\u002Fself-hosting-maintenance","Maintenance & Updates"," — keeping your instance up to date and troubleshooting",[1159,1160,1161],"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":137,"searchDepth":180,"depth":180,"links":1163},[1164,1165,1166,1167,1171,1172],{"id":16,"depth":180,"text":17},{"id":49,"depth":180,"text":50},{"id":126,"depth":180,"text":127},{"id":266,"depth":180,"text":267,"children":1168},[1169,1170],{"id":271,"depth":204,"text":272},{"id":323,"depth":204,"text":324},{"id":1006,"depth":180,"text":1007},{"id":1126,"depth":180,"text":1127},"Deploy Owlat on your own infrastructure with Docker Compose. Complete guide from first boot to production.","md",{},"\u002Fdeveloper\u002Fself-hosting",{"title":5,"description":1173},"3.developer\u002F30.self-hosting","5JcmDpKcsEIOfgduyRMvGOjziOn8fwDHX51AgTLkJVk",[1181,1185],{"title":1182,"path":1183,"stem":1184,"children":-1},"Scopes","\u002Fdeveloper\u002Fscopes","3.developer\u002F3.scopes",{"title":1186,"path":1134,"stem":1187,"children":-1},"Self-Hosting Configuration","3.developer\u002F31.self-hosting-config",1777110572728]