[{"data":1,"prerenderedAt":1221},["ShallowReactive",2],{"content-developer\u002Fself-hosting-config":3,"surround-\u002Fdeveloper\u002Fself-hosting-config":1215},{"id":4,"title":5,"body":6,"description":1207,"extension":1208,"meta":1209,"navigation":1210,"path":1211,"seo":1212,"stem":1213,"__hash__":1214},"content\u002F3.developer\u002F31.self-hosting-config.md","Self-Hosting Configuration",{"type":7,"value":8,"toc":1190},"minimark",[9,19,24,61,65,71,76,138,142,145,212,216,400,404,407,557,561,602,606,609,679,682,802,806,880,884,960,967,971,1123,1127,1186],[10,11,12,13,18],"p",{},"This page is the complete configuration reference for a self-hosted Owlat instance. For step-by-step setup, see ",[14,15,17],"a",{"href":16},"\u002Fdeveloper\u002Fself-hosting","Self-Hosting",".",[20,21,23],"h2",{"id":22},"two-configuration-layers","Two Configuration Layers",[25,26,29,37],"callout",{"title":27,"type":28},"Docker .env vs Convex env vars","warning",[10,30,31,32,36],{},"Owlat uses ",[33,34,35],"strong",{},"two separate configuration layers",". Mixing them up is the most common setup mistake.",[38,39,40,51],"ul",{},[41,42,43,50],"li",{},[33,44,45,46],{},"Docker ",[47,48,49],"code",{},".env"," — read by Docker Compose at container startup. Controls ports, secrets shared between containers, and browser-facing URLs.",[41,52,53,56,57,60],{},[33,54,55],{},"Convex environment variables"," — read by serverless functions inside the Convex backend. Set after deployment via ",[47,58,59],{},"npx convex env set",". Controls application behavior: email provider, auth secrets, sender identity, integrations.",[20,62,64],{"id":63},"docker-environment-variables","Docker Environment Variables",[10,66,67,68,70],{},"These go in the ",[47,69,49],{}," file at the project root. Docker Compose reads them when starting containers.",[72,73,75],"h3",{"id":74},"convex-backend","Convex Backend",[77,78,79,98],"table",{},[80,81,82],"thead",{},[83,84,85,89,92,95],"tr",{},[86,87,88],"th",{},"Variable",[86,90,91],{},"Required",[86,93,94],{},"Default",[86,96,97],{},"Description",[99,100,101,121],"tbody",{},[83,102,103,109,112,115],{},[104,105,106],"td",{},[47,107,108],{},"INSTANCE_SECRET",[104,110,111],{},"Yes",[104,113,114],{},"—",[104,116,117,118,18],{},"Convex backend instance identity. Generate with ",[47,119,120],{},"openssl rand -hex 32",[83,122,123,128,130,132],{},[104,124,125],{},[47,126,127],{},"CONVEX_ADMIN_KEY",[104,129,111],{},[104,131,114],{},[104,133,134,135,18],{},"Admin key for deploying functions. Generated after first boot via ",[47,136,137],{},"docker compose exec convex .\u002Fgenerate_admin_key.sh",[72,139,141],{"id":140},"public-urls","Public URLs",[10,143,144],{},"These must be reachable from the user's browser — do not use Docker-internal hostnames.",[77,146,147,159],{},[80,148,149],{},[83,150,151,153,155,157],{},[86,152,88],{},[86,154,91],{},[86,156,94],{},[86,158,97],{},[99,160,161,178,195],{},[83,162,163,168,170,175],{},[104,164,165],{},[47,166,167],{},"NUXT_PUBLIC_CONVEX_URL",[104,169,111],{},[104,171,172],{},[47,173,174],{},"http:\u002F\u002Flocalhost:3210",[104,176,177],{},"Convex backend URL (browser → Convex).",[83,179,180,185,187,192],{},[104,181,182],{},[47,183,184],{},"NUXT_PUBLIC_CONVEX_SITE_URL",[104,186,111],{},[104,188,189],{},[47,190,191],{},"http:\u002F\u002Flocalhost:3211",[104,193,194],{},"Convex site proxy URL (browser → HTTP actions).",[83,196,197,202,204,209],{},[104,198,199],{},[47,200,201],{},"NUXT_PUBLIC_SITE_URL",[104,203,111],{},[104,205,206],{},[47,207,208],{},"http:\u002F\u002Flocalhost:3000",[104,210,211],{},"Web application URL.",[72,213,215],{"id":214},"mta-configuration","MTA Configuration",[77,217,218,230],{},[80,219,220],{},[83,221,222,224,226,228],{},[86,223,88],{},[86,225,91],{},[86,227,94],{},[86,229,97],{},[99,231,232,249,265,282,299,317,333,354,371],{},[83,233,234,239,241,243],{},[104,235,236],{},[47,237,238],{},"MTA_API_KEY",[104,240,111],{},[104,242,114],{},[104,244,245,246,18],{},"Shared secret for Convex → MTA authentication. Generate with ",[47,247,248],{},"openssl rand -base64 32",[83,250,251,256,258,260],{},[104,252,253],{},[47,254,255],{},"MTA_WEBHOOK_SECRET",[104,257,111],{},[104,259,114],{},[104,261,262,263,18],{},"HMAC secret for MTA → Convex webhook callbacks. Generate with ",[47,264,248],{},[83,266,267,272,274,279],{},[104,268,269],{},[47,270,271],{},"EHLO_HOSTNAME",[104,273,111],{},[104,275,276],{},[47,277,278],{},"mail.localhost",[104,280,281],{},"SMTP EHLO\u002FHELO hostname. Must match your server's rDNS PTR record in production.",[83,283,284,289,291,296],{},[104,285,286],{},[47,287,288],{},"RETURN_PATH_DOMAIN",[104,290,111],{},[104,292,293],{},[47,294,295],{},"bounces.localhost",[104,297,298],{},"Domain for VERP bounce return-path addresses. Needs an MX record pointing to your server.",[83,300,301,306,309,314],{},[104,302,303],{},[47,304,305],{},"IP_POOLS_TRANSACTIONAL",[104,307,308],{},"No",[104,310,311],{},[47,312,313],{},"127.0.0.1",[104,315,316],{},"Comma-separated IPs for transactional email delivery.",[83,318,319,324,326,330],{},[104,320,321],{},[47,322,323],{},"IP_POOLS_CAMPAIGN",[104,325,308],{},[104,327,328],{},[47,329,313],{},[104,331,332],{},"Comma-separated IPs for campaign\u002Fmarketing email delivery.",[83,334,335,340,342,347],{},[104,336,337],{},[47,338,339],{},"DKIM_KEYS",[104,341,308],{},[104,343,344],{},[47,345,346],{},"{}",[104,348,349,350,18],{},"DKIM signing keys as JSON. See ",[14,351,353],{"href":352},"\u002Fdeveloper\u002Fself-hosting-dns-email#dkim","DNS & Email Setup",[83,355,356,361,363,368],{},[104,357,358],{},[47,359,360],{},"WORKER_CONCURRENCY",[104,362,308],{},[104,364,365],{},[47,366,367],{},"50",[104,369,370],{},"Number of parallel MTA worker threads.",[83,372,373,378,380,385],{},[104,374,375],{},[47,376,377],{},"MTA_LOG_LEVEL",[104,379,308],{},[104,381,382],{},[47,383,384],{},"info",[104,386,387,388,391,392,391,394,391,397,18],{},"MTA log verbosity: ",[47,389,390],{},"debug",", ",[47,393,384],{},[47,395,396],{},"warn",[47,398,399],{},"error",[72,401,403],{"id":402},"port-overrides","Port Overrides",[10,405,406],{},"All ports can be changed if the defaults conflict with existing services.",[77,408,409,420],{},[80,410,411],{},[83,412,413,415,417],{},[86,414,88],{},[86,416,94],{},[86,418,419],{},"Service",[99,421,422,437,452,467,482,497,512,527,542],{},[83,423,424,429,434],{},[104,425,426],{},[47,427,428],{},"CONVEX_PORT",[104,430,431],{},[47,432,433],{},"3210",[104,435,436],{},"Convex backend API",[83,438,439,444,449],{},[104,440,441],{},[47,442,443],{},"CONVEX_SITE_PORT",[104,445,446],{},[47,447,448],{},"3211",[104,450,451],{},"Convex HTTP actions",[83,453,454,459,464],{},[104,455,456],{},[47,457,458],{},"DASHBOARD_PORT",[104,460,461],{},[47,462,463],{},"6791",[104,465,466],{},"Convex dashboard",[83,468,469,474,479],{},[104,470,471],{},[47,472,473],{},"WEB_PORT",[104,475,476],{},[47,477,478],{},"3000",[104,480,481],{},"Web application",[83,483,484,489,494],{},[104,485,486],{},[47,487,488],{},"MTA_HTTP_PORT",[104,490,491],{},[47,492,493],{},"3100",[104,495,496],{},"MTA HTTP API",[83,498,499,504,509],{},[104,500,501],{},[47,502,503],{},"MTA_SMTP_PORT",[104,505,506],{},[47,507,508],{},"25",[104,510,511],{},"MTA SMTP (bounce processing)",[83,513,514,519,524],{},[104,515,516],{},[47,517,518],{},"REDIS_PORT",[104,520,521],{},[47,522,523],{},"6379",[104,525,526],{},"Redis",[83,528,529,534,539],{},[104,530,531],{},[47,532,533],{},"CLAMAV_PORT",[104,535,536],{},[47,537,538],{},"3310",[104,540,541],{},"ClamAV",[83,543,544,549,554],{},[104,545,546],{},[47,547,548],{},"NEST_PORT",[104,550,551],{},[47,552,553],{},"3001",[104,555,556],{},"Admin panel",[72,558,560],{"id":559},"analytics-optional","Analytics (Optional)",[77,562,563,573],{},[80,564,565],{},[83,566,567,569,571],{},[86,568,88],{},[86,570,94],{},[86,572,97],{},[99,574,575,587],{},[83,576,577,582,584],{},[104,578,579],{},[47,580,581],{},"NUXT_PUBLIC_POSTHOG_API_KEY",[104,583,114],{},[104,585,586],{},"PostHog project API key for client-side tracking.",[83,588,589,594,599],{},[104,590,591],{},[47,592,593],{},"NUXT_PUBLIC_POSTHOG_HOST",[104,595,596],{},[47,597,598],{},"https:\u002F\u002Feu.i.posthog.com",[104,600,601],{},"PostHog instance URL.",[20,603,605],{"id":604},"convex-environment-variables","Convex Environment Variables",[10,607,608],{},"These are set after deploying functions. They control application-level behavior inside the Convex backend.",[610,611,616],"pre",{"className":612,"code":613,"language":614,"meta":615,"style":615},"language-bash shiki shiki-themes github-light github-dark-dimmed","npx convex env set VAR_NAME \"value\" \\\n  --url http:\u002F\u002Flocalhost:3210 \\\n  --admin-key \u003Cyour-admin-key>\n","bash","",[47,617,618,647,659],{"__ignoreMap":615},[619,620,623,627,631,634,637,640,643],"span",{"class":621,"line":622},"line",1,[619,624,626],{"class":625},"sOLd2","npx",[619,628,630],{"class":629},"s-HuK"," convex",[619,632,633],{"class":629}," env",[619,635,636],{"class":629}," set",[619,638,639],{"class":629}," VAR_NAME",[619,641,642],{"class":629}," \"value\"",[619,644,646],{"class":645},"s74oq"," \\\n",[619,648,650,654,657],{"class":621,"line":649},2,[619,651,653],{"class":652},"sviXB","  --url",[619,655,656],{"class":629}," http:\u002F\u002Flocalhost:3210",[619,658,646],{"class":645},[619,660,662,665,669,672,676],{"class":621,"line":661},3,[619,663,664],{"class":652},"  --admin-key",[619,666,668],{"class":667},"s7YZ4"," \u003C",[619,670,671],{"class":629},"your-admin-ke",[619,673,675],{"class":674},"sYgZi","y",[619,677,678],{"class":667},">\n",[72,680,91],{"id":681},"required",[77,683,684,692],{},[80,685,686],{},[83,687,688,690],{},[86,689,88],{},[86,691,97],{},[99,693,694,707,717,729,741,762,776,790],{},[83,695,696,701],{},[104,697,698],{},[47,699,700],{},"SITE_URL",[104,702,703,704,706],{},"Public site URL for auth redirects (e.g., ",[47,705,208],{},").",[83,708,709,714],{},[104,710,711],{},[47,712,713],{},"CONVEX_SITE_URL",[104,715,716],{},"Convex site URL for tracking pixels and unsubscribe links.",[83,718,719,724],{},[104,720,721],{},[47,722,723],{},"BETTER_AUTH_SECRET",[104,725,726,727,18],{},"Secret for signing auth sessions. Generate with ",[47,728,248],{},[83,730,731,736],{},[104,732,733],{},[47,734,735],{},"UNSUBSCRIBE_SECRET",[104,737,738,739,18],{},"HMAC secret for signed unsubscribe tokens. Generate with ",[47,740,248],{},[83,742,743,748],{},[104,744,745],{},[47,746,747],{},"EMAIL_PROVIDER",[104,749,750,751,754,755,758,759,18],{},"Email provider: ",[47,752,753],{},"mta"," (default), ",[47,756,757],{},"ses",", or ",[47,760,761],{},"resend",[83,763,764,769],{},[104,765,766],{},[47,767,768],{},"MTA_API_URL",[104,770,771,772,775],{},"MTA service URL. Use ",[47,773,774],{},"http:\u002F\u002Fmta:3100"," for Docker networking.",[83,777,778,782],{},[104,779,780],{},[47,781,238],{},[104,783,784,785,787,788,18],{},"Must match the ",[47,786,238],{}," in Docker ",[47,789,49],{},[83,791,792,796],{},[104,793,794],{},[47,795,255],{},[104,797,784,798,787,800,18],{},[47,799,255],{},[47,801,49],{},[72,803,805],{"id":804},"sender-identity","Sender Identity",[77,807,808,818],{},[80,809,810],{},[83,811,812,814,816],{},[86,813,88],{},[86,815,94],{},[86,817,97],{},[99,819,820,835,850,865],{},[83,821,822,827,832],{},[104,823,824],{},[47,825,826],{},"DEFAULT_FROM_EMAIL",[104,828,829],{},[47,830,831],{},"noreply@example.com",[104,833,834],{},"Default sender email address.",[83,836,837,842,847],{},[104,838,839],{},[47,840,841],{},"DEFAULT_FROM_NAME",[104,843,844],{},[47,845,846],{},"Owlat",[104,848,849],{},"Default sender display name.",[83,851,852,857,862],{},[104,853,854],{},[47,855,856],{},"DEFAULT_FROM_DOMAIN",[104,858,859],{},[47,860,861],{},"mail.owlat.app",[104,863,864],{},"Domain for system emails (invitations, etc.).",[83,866,867,872,874],{},[104,868,869],{},[47,870,871],{},"ALLOWED_ORIGINS",[104,873,114],{},[104,875,876,877,706],{},"Comma-separated CORS origins (e.g., ",[47,878,879],{},"http:\u002F\u002Flocalhost:3000,http:\u002F\u002Flocalhost:3001",[72,881,883],{"id":882},"optional-integrations","Optional Integrations",[77,885,886,894],{},[80,887,888],{},[83,889,890,892],{},[86,891,88],{},[86,893,97],{},[99,895,896,906,918,928,940,950],{},[83,897,898,903],{},[104,899,900],{},[47,901,902],{},"GOOGLE_SAFE_BROWSING_API_KEY",[104,904,905],{},"Google Safe Browsing API v4 key for URL reputation checking.",[83,907,908,913],{},[104,909,910],{},[47,911,912],{},"MTA_INTERNAL_URL",[104,914,915,916,706],{},"MTA URL for ClamAV attachment scanning (e.g., ",[47,917,774],{},[83,919,920,925],{},[104,921,922],{},[47,923,924],{},"POSTHOG_API_KEY",[104,926,927],{},"PostHog API key for server-side analytics.",[83,929,930,935],{},[104,931,932],{},[47,933,934],{},"POSTHOG_HOST",[104,936,937,938,706],{},"PostHog instance URL (default: ",[47,939,598],{},[83,941,942,947],{},[104,943,944],{},[47,945,946],{},"OPENROUTER_API_KEY",[104,948,949],{},"OpenRouter API key for AI-powered translations.",[83,951,952,957],{},[104,953,954],{},[47,955,956],{},"OPENAI_API_KEY",[104,958,959],{},"OpenAI API key for AI translations (fallback).",[10,961,962,963,18],{},"For the complete variable reference including AWS SES, Resend, and control plane variables, see ",[14,964,966],{"href":965},"\u002Fdeveloper\u002Fenvironment-variables","Environment Variables",[20,968,970],{"id":969},"service-topology","Service Topology",[77,972,973,988],{},[80,974,975],{},[83,976,977,979,982,985],{},[86,978,419],{},[86,980,981],{},"Ports",[86,983,984],{},"Depends On",[86,986,987],{},"Healthcheck",[99,989,990,1008,1022,1035,1049,1066,1083,1096,1110],{},[83,991,992,997,1000,1002],{},[104,993,994],{},[33,995,996],{},"convex",[104,998,999],{},"3210 (API), 3211 (site proxy)",[104,1001,114],{},[104,1003,1004,1007],{},[47,1005,1006],{},"curl -f http:\u002F\u002Flocalhost:3210\u002Fversion"," every 15s",[83,1009,1010,1015,1017,1020],{},[104,1011,1012],{},[33,1013,1014],{},"convex-dashboard",[104,1016,463],{},[104,1018,1019],{},"convex (healthy)",[104,1021,114],{},[83,1023,1024,1029,1031,1033],{},[104,1025,1026],{},[33,1027,1028],{},"web",[104,1030,478],{},[104,1032,1019],{},[104,1034,114],{},[83,1036,1037,1041,1044,1047],{},[104,1038,1039],{},[33,1040,753],{},[104,1042,1043],{},"3100 (HTTP), 25 (SMTP)",[104,1045,1046],{},"redis (healthy), clamav (healthy), convex (healthy)",[104,1048,114],{},[83,1050,1051,1056,1058,1060],{},[104,1052,1053],{},[33,1054,1055],{},"redis",[104,1057,523],{},[104,1059,114],{},[104,1061,1062,1065],{},[47,1063,1064],{},"redis-cli ping"," every 10s",[83,1067,1068,1073,1075,1077],{},[104,1069,1070],{},[33,1071,1072],{},"clamav",[104,1074,538],{},[104,1076,114],{},[104,1078,1079,1082],{},[47,1080,1081],{},"clamdcheck"," every 60s (120s start delay)",[83,1084,1085,1090,1092,1094],{},[104,1086,1087],{},[33,1088,1089],{},"nest",[104,1091,553],{},[104,1093,1019],{},[104,1095,114],{},[83,1097,1098,1103,1105,1107],{},[104,1099,1100],{},[33,1101,1102],{},"convex-deploy",[104,1104,114],{},[104,1106,1019],{},[104,1108,1109],{},"One-shot (deploy profile)",[83,1111,1112,1117,1119,1121],{},[104,1113,1114],{},[33,1115,1116],{},"nest-api-deploy",[104,1118,114],{},[104,1120,1019],{},[104,1122,1109],{},[20,1124,1126],{"id":1125},"volumes","Volumes",[77,1128,1129,1142],{},[80,1130,1131],{},[83,1132,1133,1136,1139],{},[86,1134,1135],{},"Volume",[86,1137,1138],{},"Persists",[86,1140,1141],{},"Backup Priority",[99,1143,1144,1160,1173],{},[83,1145,1146,1151,1154],{},[104,1147,1148],{},[47,1149,1150],{},"convex-data",[104,1152,1153],{},"Database, file storage, vector indexes",[104,1155,1156,1159],{},[33,1157,1158],{},"Critical"," — all application data",[83,1161,1162,1167,1170],{},[104,1163,1164],{},[47,1165,1166],{},"redis-data",[104,1168,1169],{},"MTA job queue (AOF)",[104,1171,1172],{},"Medium — in-flight email jobs",[83,1174,1175,1180,1183],{},[104,1176,1177],{},[47,1178,1179],{},"clamav-data",[104,1181,1182],{},"Virus definition signatures",[104,1184,1185],{},"Low — re-downloads automatically",[1187,1188,1189],"style",{},"html pre.shiki code .sOLd2, html code.shiki .sOLd2{--shiki-default:#6F42C1;--shiki-dark:#F69D50}html pre.shiki code .s-HuK, html code.shiki .s-HuK{--shiki-default:#032F62;--shiki-dark:#96D0FF}html pre.shiki code .s74oq, html code.shiki .s74oq{--shiki-default:#005CC5;--shiki-dark:#F47067}html pre.shiki code .sviXB, html code.shiki .sviXB{--shiki-default:#005CC5;--shiki-dark:#6CB6FF}html pre.shiki code .s7YZ4, html code.shiki .s7YZ4{--shiki-default:#D73A49;--shiki-dark:#F47067}html pre.shiki code .sYgZi, html code.shiki .sYgZi{--shiki-default:#24292E;--shiki-dark:#ADBAC7}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);}",{"title":615,"searchDepth":649,"depth":649,"links":1191},[1192,1193,1200,1205,1206],{"id":22,"depth":649,"text":23},{"id":63,"depth":649,"text":64,"children":1194},[1195,1196,1197,1198,1199],{"id":74,"depth":661,"text":75},{"id":140,"depth":661,"text":141},{"id":214,"depth":661,"text":215},{"id":402,"depth":661,"text":403},{"id":559,"depth":661,"text":560},{"id":604,"depth":649,"text":605,"children":1201},[1202,1203,1204],{"id":681,"depth":661,"text":91},{"id":804,"depth":661,"text":805},{"id":882,"depth":661,"text":883},{"id":969,"depth":649,"text":970},{"id":1125,"depth":649,"text":1126},"Complete reference for Docker environment variables, Convex backend variables, service topology, and volume persistence.","md",{},true,"\u002Fdeveloper\u002Fself-hosting-config",{"title":5,"description":1207},"3.developer\u002F31.self-hosting-config","p0DjDxgzdoVb4B00M2iS7uvkKclcsVb_VPNizwi2Kxg",[1216,1218],{"title":17,"path":16,"stem":1217,"children":-1},"3.developer\u002F30.self-hosting",{"title":353,"path":1219,"stem":1220,"children":-1},"\u002Fdeveloper\u002Fself-hosting-dns-email","3.developer\u002F32.self-hosting-dns-email",1777110578501]