[{"data":1,"prerenderedAt":1590},["ShallowReactive",2],{"content-developer\u002Fself-hosting-maintenance":3,"surround-\u002Fdeveloper\u002Fself-hosting-maintenance":1584},{"id":4,"title":5,"body":6,"description":1577,"extension":1578,"meta":1579,"navigation":187,"path":1580,"seo":1581,"stem":1582,"__hash__":1583},"content\u002F3.developer\u002F34.self-hosting-maintenance.md","Maintenance & Updates",{"type":7,"value":8,"toc":1545},"minimark",[9,19,24,40,45,56,77,80,88,95,138,141,145,148,298,306,310,317,321,409,413,419,424,445,450,529,536,540,597,601,608,636,642,646,653,668,675,679,682,750,758,762,765,852,859,863,870,873,893,896,917,921,924,952,956,960,971,991,997,1001,1069,1073,1079,1105,1109,1113,1128,1131,1160,1164,1179,1181,1216,1220,1227,1234,1238,1249,1255,1270,1274,1297,1299,1325,1329,1332,1541],[10,11,12,13,18],"p",{},"This guide covers day-to-day maintenance of your self-hosted Owlat instance. For initial setup, see ",[14,15,17],"a",{"href":16},"\u002Fdeveloper\u002Fself-hosting","Self-Hosting",".",[20,21,23],"h2",{"id":22},"updating","Updating",[10,25,26,27,31,32,35,36,39],{},"Owlat supports three ways to update: ",[28,29,30],"strong",{},"in-app",", ",[28,33,34],{},"CLI",", or ",[28,37,38],{},"manual",". All three do the same thing under the hood — pull the pinned compose template for a tagged release, apply it, and redeploy Convex functions.",[41,42,44],"h3",{"id":43},"option-a-in-app-update-recommended","Option A: In-app update (recommended)",[10,46,47,48,51,52,55],{},"Platform admins see an \"Update available\" notification at ",[28,49,50],{},"Settings → System & Updates"," when a new version is released. Click ",[28,53,54],{},"Update now",", confirm, and the web app will:",[57,58,59,68,71,74],"ol",{},[60,61,62,63,67],"li",{},"Download the pinned ",[64,65,66],"code",{},"docker-compose-\u003Cversion>.yml"," from GitHub Releases",[60,69,70],{},"Dispatch it to the updater sidecar",[60,72,73],{},"Pull new images, recreate containers, redeploy Convex functions",[60,75,76],{},"Verify the new version is live",[10,78,79],{},"The page polls container health and auto-reloads once the update is complete. Total time: typically 2–5 minutes.",[41,81,83,84,87],{"id":82},"option-b-owlat-upgrade-cli","Option B: ",[64,85,86],{},"owlat upgrade"," CLI",[10,89,90,91,94],{},"Installed to ",[64,92,93],{},"\u002Fusr\u002Flocal\u002Fbin\u002Fowlat"," by the installer:",[96,97,102],"pre",{"className":98,"code":99,"language":100,"meta":101,"style":101},"language-bash shiki shiki-themes github-light github-dark-dimmed","owlat upgrade                     # to latest\nowlat upgrade --version 1.2.3     # to a specific version (useful for rollback)\n","bash","",[64,103,104,121],{"__ignoreMap":101},[105,106,109,113,117],"span",{"class":107,"line":108},"line",1,[105,110,112],{"class":111},"sOLd2","owlat",[105,114,116],{"class":115},"s-HuK"," upgrade",[105,118,120],{"class":119},"sDN9O","                     # to latest\n",[105,122,124,126,128,132,135],{"class":107,"line":123},2,[105,125,112],{"class":111},[105,127,116],{"class":115},[105,129,131],{"class":130},"sviXB"," --version",[105,133,134],{"class":130}," 1.2.3",[105,136,137],{"class":119},"     # to a specific version (useful for rollback)\n",[10,139,140],{},"The CLI drives the same updater sidecar the in-app flow uses.",[41,142,144],{"id":143},"option-c-manual","Option C: Manual",[10,146,147],{},"For air-gapped environments or if you prefer to drive updates yourself:",[96,149,151],{"className":98,"code":150,"language":100,"meta":101,"style":101},"# 1. Pull the pinned compose template for the target version\ncurl -fsSL https:\u002F\u002Fgithub.com\u002Fowlat\u002Fowlat\u002Freleases\u002Fdownload\u002Fv1.2.3\u002Fdocker-compose-1.2.3.yml \\\n  -o docker-compose.yml\n\n# 2. Pull the new images\ndocker compose pull\n\n# 3. Recreate containers\ndocker compose up -d\n\n# 4. Re-deploy Convex tenant functions\ndocker compose --profile deploy run --rm convex-deploy\n\n# 5. (Hosted-cloud operators only) Re-deploy control-plane functions\ndocker compose --profile hosted --profile deploy run --rm nest-api-deploy\n",[64,152,153,158,173,182,189,195,207,212,218,231,236,242,264,269,275],{"__ignoreMap":101},[105,154,155],{"class":107,"line":108},[105,156,157],{"class":119},"# 1. Pull the pinned compose template for the target version\n",[105,159,160,163,166,169],{"class":107,"line":123},[105,161,162],{"class":111},"curl",[105,164,165],{"class":130}," -fsSL",[105,167,168],{"class":115}," https:\u002F\u002Fgithub.com\u002Fowlat\u002Fowlat\u002Freleases\u002Fdownload\u002Fv1.2.3\u002Fdocker-compose-1.2.3.yml",[105,170,172],{"class":171},"s74oq"," \\\n",[105,174,176,179],{"class":107,"line":175},3,[105,177,178],{"class":130},"  -o",[105,180,181],{"class":115}," docker-compose.yml\n",[105,183,185],{"class":107,"line":184},4,[105,186,188],{"emptyLinePlaceholder":187},true,"\n",[105,190,192],{"class":107,"line":191},5,[105,193,194],{"class":119},"# 2. Pull the new images\n",[105,196,198,201,204],{"class":107,"line":197},6,[105,199,200],{"class":111},"docker",[105,202,203],{"class":115}," compose",[105,205,206],{"class":115}," pull\n",[105,208,210],{"class":107,"line":209},7,[105,211,188],{"emptyLinePlaceholder":187},[105,213,215],{"class":107,"line":214},8,[105,216,217],{"class":119},"# 3. Recreate containers\n",[105,219,221,223,225,228],{"class":107,"line":220},9,[105,222,200],{"class":111},[105,224,203],{"class":115},[105,226,227],{"class":115}," up",[105,229,230],{"class":130}," -d\n",[105,232,234],{"class":107,"line":233},10,[105,235,188],{"emptyLinePlaceholder":187},[105,237,239],{"class":107,"line":238},11,[105,240,241],{"class":119},"# 4. Re-deploy Convex tenant functions\n",[105,243,245,247,249,252,255,258,261],{"class":107,"line":244},12,[105,246,200],{"class":111},[105,248,203],{"class":115},[105,250,251],{"class":130}," --profile",[105,253,254],{"class":115}," deploy",[105,256,257],{"class":115}," run",[105,259,260],{"class":130}," --rm",[105,262,263],{"class":115}," convex-deploy\n",[105,265,267],{"class":107,"line":266},13,[105,268,188],{"emptyLinePlaceholder":187},[105,270,272],{"class":107,"line":271},14,[105,273,274],{"class":119},"# 5. (Hosted-cloud operators only) Re-deploy control-plane functions\n",[105,276,278,280,282,284,287,289,291,293,295],{"class":107,"line":277},15,[105,279,200],{"class":111},[105,281,203],{"class":115},[105,283,251],{"class":130},[105,285,286],{"class":115}," hosted",[105,288,251],{"class":130},[105,290,254],{"class":115},[105,292,257],{"class":115},[105,294,260],{"class":130},[105,296,297],{"class":115}," nest-api-deploy\n",[299,300,303],"callout",{"title":301,"type":302},"Always re-deploy functions","warning",[10,304,305],{},"Pulling new Docker images updates the containers, but the Convex serverless functions are deployed separately. The in-app and CLI paths run this for you; the manual path requires step 4 above.",[20,307,309],{"id":308},"recovering-from-a-failed-update","Recovering from a failed update",[10,311,312,313,316],{},"If an update fails mid-flight, your stack may be in a mixed state — some containers on the new version, some on the old. ",[28,314,315],{},"Your data is not at risk",": Convex's LMDB storage and Redis's AOF log are both crash-safe, and the updater never touches the volumes.",[41,318,320],{"id":319},"diagnose-first","Diagnose first",[96,322,324],{"className":98,"code":323,"language":100,"meta":101,"style":101},"# What's running?\nowlat status                       # or: docker compose ps\n\n# What did each container log?\nowlat logs web                     # or: docker compose logs --tail=200 web\nowlat logs mta\nowlat logs convex\nowlat logs updater\n\n# Environment health check\nowlat doctor                       # checks DNS, SMTP egress, secrets, containers\n",[64,325,326,331,341,345,350,363,372,381,390,394,399],{"__ignoreMap":101},[105,327,328],{"class":107,"line":108},[105,329,330],{"class":119},"# What's running?\n",[105,332,333,335,338],{"class":107,"line":123},[105,334,112],{"class":111},[105,336,337],{"class":115}," status",[105,339,340],{"class":119},"                       # or: docker compose ps\n",[105,342,343],{"class":107,"line":175},[105,344,188],{"emptyLinePlaceholder":187},[105,346,347],{"class":107,"line":184},[105,348,349],{"class":119},"# What did each container log?\n",[105,351,352,354,357,360],{"class":107,"line":191},[105,353,112],{"class":111},[105,355,356],{"class":115}," logs",[105,358,359],{"class":115}," web",[105,361,362],{"class":119},"                     # or: docker compose logs --tail=200 web\n",[105,364,365,367,369],{"class":107,"line":197},[105,366,112],{"class":111},[105,368,356],{"class":115},[105,370,371],{"class":115}," mta\n",[105,373,374,376,378],{"class":107,"line":209},[105,375,112],{"class":111},[105,377,356],{"class":115},[105,379,380],{"class":115}," convex\n",[105,382,383,385,387],{"class":107,"line":214},[105,384,112],{"class":111},[105,386,356],{"class":115},[105,388,389],{"class":115}," updater\n",[105,391,392],{"class":107,"line":220},[105,393,188],{"emptyLinePlaceholder":187},[105,395,396],{"class":107,"line":233},[105,397,398],{"class":119},"# Environment health check\n",[105,400,401,403,406],{"class":107,"line":238},[105,402,112],{"class":111},[105,404,405],{"class":115}," doctor",[105,407,408],{"class":119},"                       # checks DNS, SMTP egress, secrets, containers\n",[41,410,412],{"id":411},"rollback-to-the-previous-version","Rollback to the previous version",[10,414,415,416,418],{},"Every tagged release attaches its ",[64,417,66],{}," to the GitHub Release page. To roll back:",[10,420,421],{},[28,422,423],{},"Via CLI:",[96,425,427],{"className":98,"code":426,"language":100,"meta":101,"style":101},"# Replace with the last-known-good version\nowlat upgrade --version 1.2.2\n",[64,428,429,434],{"__ignoreMap":101},[105,430,431],{"class":107,"line":108},[105,432,433],{"class":119},"# Replace with the last-known-good version\n",[105,435,436,438,440,442],{"class":107,"line":123},[105,437,112],{"class":111},[105,439,116],{"class":115},[105,441,131],{"class":130},[105,443,444],{"class":130}," 1.2.2\n",[10,446,447],{},[28,448,449],{},"Manually:",[96,451,453],{"className":98,"code":452,"language":100,"meta":101,"style":101},"# Download the previous release's compose file\ncurl -fsSL https:\u002F\u002Fgithub.com\u002Fowlat\u002Fowlat\u002Freleases\u002Fdownload\u002Fv1.2.2\u002Fdocker-compose-1.2.2.yml \\\n  -o docker-compose.yml\n\n# Pull the pinned images and recreate\ndocker compose pull\ndocker compose up -d\n\n# Re-deploy functions at the old version\ndocker compose --profile deploy run --rm convex-deploy\n",[64,454,455,460,471,477,481,486,494,504,508,513],{"__ignoreMap":101},[105,456,457],{"class":107,"line":108},[105,458,459],{"class":119},"# Download the previous release's compose file\n",[105,461,462,464,466,469],{"class":107,"line":123},[105,463,162],{"class":111},[105,465,165],{"class":130},[105,467,468],{"class":115}," https:\u002F\u002Fgithub.com\u002Fowlat\u002Fowlat\u002Freleases\u002Fdownload\u002Fv1.2.2\u002Fdocker-compose-1.2.2.yml",[105,470,172],{"class":171},[105,472,473,475],{"class":107,"line":175},[105,474,178],{"class":130},[105,476,181],{"class":115},[105,478,479],{"class":107,"line":184},[105,480,188],{"emptyLinePlaceholder":187},[105,482,483],{"class":107,"line":191},[105,484,485],{"class":119},"# Pull the pinned images and recreate\n",[105,487,488,490,492],{"class":107,"line":197},[105,489,200],{"class":111},[105,491,203],{"class":115},[105,493,206],{"class":115},[105,495,496,498,500,502],{"class":107,"line":209},[105,497,200],{"class":111},[105,499,203],{"class":115},[105,501,227],{"class":115},[105,503,230],{"class":130},[105,505,506],{"class":107,"line":214},[105,507,188],{"emptyLinePlaceholder":187},[105,509,510],{"class":107,"line":220},[105,511,512],{"class":119},"# Re-deploy functions at the old version\n",[105,514,515,517,519,521,523,525,527],{"class":107,"line":233},[105,516,200],{"class":111},[105,518,203],{"class":115},[105,520,251],{"class":130},[105,522,254],{"class":115},[105,524,257],{"class":115},[105,526,260],{"class":130},[105,528,263],{"class":115},[10,530,531,532,535],{},"Docker will pull images for the pinned tags (e.g. ",[64,533,534],{},"ghcr.io\u002Fowlat\u002Fweb:1.2.2",") and swap containers in-place.",[41,537,539],{"id":538},"if-the-web-container-wont-start","If the web container won't start",[96,541,543],{"className":98,"code":542,"language":100,"meta":101,"style":101},"# See why\ndocker compose logs web\n\n# Rule out a stale image\ndocker compose pull web\ndocker compose up -d --force-recreate web\n",[64,544,545,550,561,565,570,581],{"__ignoreMap":101},[105,546,547],{"class":107,"line":108},[105,548,549],{"class":119},"# See why\n",[105,551,552,554,556,558],{"class":107,"line":123},[105,553,200],{"class":111},[105,555,203],{"class":115},[105,557,356],{"class":115},[105,559,560],{"class":115}," web\n",[105,562,563],{"class":107,"line":175},[105,564,188],{"emptyLinePlaceholder":187},[105,566,567],{"class":107,"line":184},[105,568,569],{"class":119},"# Rule out a stale image\n",[105,571,572,574,576,579],{"class":107,"line":191},[105,573,200],{"class":111},[105,575,203],{"class":115},[105,577,578],{"class":115}," pull",[105,580,560],{"class":115},[105,582,583,585,587,589,592,595],{"class":107,"line":197},[105,584,200],{"class":111},[105,586,203],{"class":115},[105,588,227],{"class":115},[105,590,591],{"class":130}," -d",[105,593,594],{"class":130}," --force-recreate",[105,596,560],{"class":115},[41,598,600],{"id":599},"if-convex-wont-start","If Convex won't start",[10,602,603,604,607],{},"Convex is the only stateful service in the stack besides Redis and ClamAV. If it's failing to start, check disk space on the ",[64,605,606],{},"convex-data"," volume first:",[96,609,611],{"className":98,"code":610,"language":100,"meta":101,"style":101},"docker system df -v | grep convex-data\n",[64,612,613],{"__ignoreMap":101},[105,614,615,617,620,623,626,630,633],{"class":107,"line":108},[105,616,200],{"class":111},[105,618,619],{"class":115}," system",[105,621,622],{"class":115}," df",[105,624,625],{"class":130}," -v",[105,627,629],{"class":628},"s7YZ4"," |",[105,631,632],{"class":111}," grep",[105,634,635],{"class":115}," convex-data\n",[10,637,638,639,641],{},"If disk is full, free space or resize the underlying volume. Never delete the ",[64,640,606],{}," volume — that's your database.",[41,643,645],{"id":644},"nuclear-option-restore-from-backup","Nuclear option: restore from backup",[10,647,648,649,652],{},"If the stack is in an unrecoverable state, restore the most recent ",[64,650,651],{},"scripts\u002Fbackup.sh"," archive:",[96,654,656],{"className":98,"code":655,"language":100,"meta":101,"style":101},"bash scripts\u002Frestore.sh backups\u002Fowlat-YYYYMMDD-HHMMSS.tar.gz\n",[64,657,658],{"__ignoreMap":101},[105,659,660,662,665],{"class":107,"line":108},[105,661,100],{"class":111},[105,663,664],{"class":115}," scripts\u002Frestore.sh",[105,666,667],{"class":115}," backups\u002Fowlat-YYYYMMDD-HHMMSS.tar.gz\n",[10,669,670,671,674],{},"See the ",[28,672,673],{},"Backups"," section below for how to set up automated backups.",[41,676,678],{"id":677},"reporting-an-update-bug","Reporting an update bug",[10,680,681],{},"If an update fails in a way you think is a bug in Owlat itself (rather than your environment), capture and attach to a GitHub issue:",[96,683,685],{"className":98,"code":684,"language":100,"meta":101,"style":101},"owlat doctor                                   > \u002Ftmp\u002Fowlat-doctor.txt 2>&1\ndocker compose ps                              > \u002Ftmp\u002Fowlat-ps.txt\ndocker compose logs --tail=500                 > \u002Ftmp\u002Fowlat-logs.txt 2>&1\ncat \u002Fopt\u002Fowlat\u002Fdocker-compose.yml              > \u002Ftmp\u002Fowlat-compose.yml\n",[64,686,687,702,717,736],{"__ignoreMap":101},[105,688,689,691,693,696,699],{"class":107,"line":108},[105,690,112],{"class":111},[105,692,405],{"class":115},[105,694,695],{"class":628},"                                   >",[105,697,698],{"class":115}," \u002Ftmp\u002Fowlat-doctor.txt",[105,700,701],{"class":628}," 2>&1\n",[105,703,704,706,708,711,714],{"class":107,"line":123},[105,705,200],{"class":111},[105,707,203],{"class":115},[105,709,710],{"class":115}," ps",[105,712,713],{"class":628},"                              >",[105,715,716],{"class":115}," \u002Ftmp\u002Fowlat-ps.txt\n",[105,718,719,721,723,725,728,731,734],{"class":107,"line":175},[105,720,200],{"class":111},[105,722,203],{"class":115},[105,724,356],{"class":115},[105,726,727],{"class":130}," --tail=500",[105,729,730],{"class":628},"                 >",[105,732,733],{"class":115}," \u002Ftmp\u002Fowlat-logs.txt",[105,735,701],{"class":628},[105,737,738,741,744,747],{"class":107,"line":184},[105,739,740],{"class":111},"cat",[105,742,743],{"class":115}," \u002Fopt\u002Fowlat\u002Fdocker-compose.yml",[105,745,746],{"class":628},"              >",[105,748,749],{"class":115}," \u002Ftmp\u002Fowlat-compose.yml\n",[10,751,752,753],{},"File at: ",[14,754,755],{"href":755,"rel":756},"https:\u002F\u002Fgithub.com\u002Fowlat\u002Fowlat\u002Fissues\u002Fnew",[757],"nofollow",[41,759,761],{"id":760},"automating-updates","Automating Updates",[10,763,764],{},"Create a systemd timer or cron job for automatic updates:",[96,766,768],{"className":98,"code":767,"language":100,"meta":101,"style":101},"# \u002Fetc\u002Fcron.d\u002Fowlat-update\n0 4 * * * root cd \u002Fopt\u002Fowlat && docker compose pull && docker compose up -d && docker compose --profile deploy run --rm convex-deploy && docker compose --profile deploy run --rm nest-api-deploy\n",[64,769,770,775],{"__ignoreMap":101},[105,771,772],{"class":107,"line":108},[105,773,774],{"class":119},"# \u002Fetc\u002Fcron.d\u002Fowlat-update\n",[105,776,777,780,783,786,788,790,793,796,799,803,805,807,809,811,813,815,817,819,821,823,825,827,829,831,833,836,838,840,842,844,846,848,850],{"class":107,"line":123},[105,778,779],{"class":111},"0",[105,781,782],{"class":130}," 4",[105,784,785],{"class":130}," *",[105,787,785],{"class":130},[105,789,785],{"class":130},[105,791,792],{"class":115}," root",[105,794,795],{"class":115}," cd",[105,797,798],{"class":115}," \u002Fopt\u002Fowlat",[105,800,802],{"class":801},"sYgZi"," && ",[105,804,200],{"class":111},[105,806,203],{"class":115},[105,808,578],{"class":115},[105,810,802],{"class":801},[105,812,200],{"class":111},[105,814,203],{"class":115},[105,816,227],{"class":115},[105,818,591],{"class":130},[105,820,802],{"class":801},[105,822,200],{"class":111},[105,824,203],{"class":115},[105,826,251],{"class":130},[105,828,254],{"class":115},[105,830,257],{"class":115},[105,832,260],{"class":130},[105,834,835],{"class":115}," convex-deploy",[105,837,802],{"class":801},[105,839,200],{"class":111},[105,841,203],{"class":115},[105,843,251],{"class":130},[105,845,254],{"class":115},[105,847,257],{"class":115},[105,849,260],{"class":130},[105,851,297],{"class":115},[299,853,856],{"title":854,"type":855},"Update safety","info",[10,857,858],{},"Convex handles database schema migrations automatically. Pulling new images and re-deploying functions is safe — the backend manages schema changes during deployment.",[20,860,862],{"id":861},"clamav-signatures","ClamAV Signatures",[10,864,865,866,869],{},"The ClamAV container runs ",[64,867,868],{},"freshclamd"," automatically, which downloads updated virus definitions daily. No manual intervention is needed.",[10,871,872],{},"To force an immediate signature update:",[96,874,876],{"className":98,"code":875,"language":100,"meta":101,"style":101},"docker compose exec clamav freshclam\n",[64,877,878],{"__ignoreMap":101},[105,879,880,882,884,887,890],{"class":107,"line":108},[105,881,200],{"class":111},[105,883,203],{"class":115},[105,885,886],{"class":115}," exec",[105,888,889],{"class":115}," clamav",[105,891,892],{"class":115}," freshclam\n",[10,894,895],{},"To check the current signature version:",[96,897,899],{"className":98,"code":898,"language":100,"meta":101,"style":101},"docker compose exec clamav clamscan --version\n",[64,900,901],{"__ignoreMap":101},[105,902,903,905,907,909,911,914],{"class":107,"line":108},[105,904,200],{"class":111},[105,906,203],{"class":115},[105,908,886],{"class":115},[105,910,889],{"class":115},[105,912,913],{"class":115}," clamscan",[105,915,916],{"class":130}," --version\n",[20,918,920],{"id":919},"redis-maintenance","Redis Maintenance",[10,922,923],{},"Redis is configured with AOF (Append Only File) persistence by default. This ensures the MTA job queue survives container restarts.",[925,926,927,933,942],"ul",{},[60,928,929,932],{},[28,930,931],{},"Compaction"," — Redis automatically rewrites the AOF file to keep it compact.",[60,934,935,938,939,18],{},[28,936,937],{},"Memory"," — monitor Redis memory usage with ",[64,940,941],{},"docker compose exec redis redis-cli info memory",[60,943,944,947,948,951],{},[28,945,946],{},"Flushing"," — if you need to clear the queue (e.g., after a misconfiguration): ",[64,949,950],{},"docker compose exec redis redis-cli FLUSHALL",". This discards all in-flight email jobs.",[20,953,955],{"id":954},"scaling","Scaling",[41,957,959],{"id":958},"mta-throughput","MTA Throughput",[10,961,962,963,966,967,970],{},"Increase ",[64,964,965],{},"WORKER_CONCURRENCY"," in your ",[64,968,969],{},".env"," to process more email groups in parallel:",[96,972,974],{"className":98,"code":973,"language":100,"meta":101,"style":101},"# Default: 50 workers\nWORKER_CONCURRENCY=100\n",[64,975,976,981],{"__ignoreMap":101},[105,977,978],{"class":107,"line":108},[105,979,980],{"class":119},"# Default: 50 workers\n",[105,982,983,985,988],{"class":107,"line":123},[105,984,965],{"class":801},[105,986,987],{"class":628},"=",[105,989,990],{"class":115},"100\n",[10,992,993,994],{},"Restart the MTA to apply: ",[64,995,996],{},"docker compose restart mta",[41,998,1000],{"id":999},"server-sizing","Server Sizing",[1002,1003,1004,1023],"table",{},[1005,1006,1007],"thead",{},[1008,1009,1010,1014,1017,1020],"tr",{},[1011,1012,1013],"th",{},"Load",[1011,1015,1016],{},"vCPU",[1011,1018,1019],{},"RAM",[1011,1021,1022],{},"Disk",[1024,1025,1026,1041,1055],"tbody",{},[1008,1027,1028,1032,1035,1038],{},[1029,1030,1031],"td",{},"Up to 10K contacts",[1029,1033,1034],{},"2",[1029,1036,1037],{},"4 GB",[1029,1039,1040],{},"40 GB",[1008,1042,1043,1046,1049,1052],{},[1029,1044,1045],{},"Up to 100K contacts",[1029,1047,1048],{},"4",[1029,1050,1051],{},"8 GB",[1029,1053,1054],{},"80 GB",[1008,1056,1057,1060,1063,1066],{},[1029,1058,1059],{},"100K+ contacts",[1029,1061,1062],{},"8",[1029,1064,1065],{},"16 GB",[1029,1067,1068],{},"160 GB",[41,1070,1072],{"id":1071},"convex-backend","Convex Backend",[10,1074,1075,1076,1078],{},"Convex is a single-node service that scales vertically. Monitor disk usage on the ",[64,1077,606],{}," volume — this is where all database records, file uploads, and vector indexes are stored.",[96,1080,1082],{"className":98,"code":1081,"language":100,"meta":101,"style":101},"# Check volume disk usage\ndocker system df -v | grep convex-data\n",[64,1083,1084,1089],{"__ignoreMap":101},[105,1085,1086],{"class":107,"line":108},[105,1087,1088],{"class":119},"# Check volume disk usage\n",[105,1090,1091,1093,1095,1097,1099,1101,1103],{"class":107,"line":123},[105,1092,200],{"class":111},[105,1094,619],{"class":115},[105,1096,622],{"class":115},[105,1098,625],{"class":130},[105,1100,629],{"class":628},[105,1102,632],{"class":111},[105,1104,635],{"class":115},[20,1106,1108],{"id":1107},"troubleshooting","Troubleshooting",[41,1110,1112],{"id":1111},"convex-wont-start","Convex won't start",[96,1114,1116],{"className":98,"code":1115,"language":100,"meta":101,"style":101},"docker compose logs convex\n",[64,1117,1118],{"__ignoreMap":101},[105,1119,1120,1122,1124,1126],{"class":107,"line":108},[105,1121,200],{"class":111},[105,1123,203],{"class":115},[105,1125,356],{"class":115},[105,1127,380],{"class":115},[10,1129,1130],{},"Common causes:",[925,1132,1133,1145,1154],{},[60,1134,1135,1141,1142,1144],{},[28,1136,1137,1140],{},[64,1138,1139],{},"INSTANCE_SECRET"," not set"," — check your ",[64,1143,969],{}," file has a valid hex string",[60,1146,1147,1150,1151,1153],{},[28,1148,1149],{},"Disk full"," — the ",[64,1152,606],{}," volume needs free space for the database",[60,1155,1156,1159],{},[28,1157,1158],{},"Port conflict"," — another service is using port 3210 or 3211",[41,1161,1163],{"id":1162},"mta-cant-send-emails","MTA can't send emails",[96,1165,1167],{"className":98,"code":1166,"language":100,"meta":101,"style":101},"docker compose logs mta\n",[64,1168,1169],{"__ignoreMap":101},[105,1170,1171,1173,1175,1177],{"class":107,"line":108},[105,1172,200],{"class":111},[105,1174,203],{"class":115},[105,1176,356],{"class":115},[105,1178,371],{"class":115},[10,1180,1130],{},[925,1182,1183,1192,1201,1207],{},[60,1184,1185,1188,1189],{},[28,1186,1187],{},"EHLO hostname doesn't match PTR record"," — receiving servers reject the connection. Verify with ",[64,1190,1191],{},"dig -x YOUR_IP +short",[60,1193,1194,1197,1198],{},[28,1195,1196],{},"DKIM_KEYS JSON is invalid"," — validate the JSON: ",[64,1199,1200],{},"echo $DKIM_KEYS | jq .",[60,1202,1203,1206],{},[28,1204,1205],{},"Port 25 blocked"," — many cloud providers (AWS, GCP, Azure) block outbound SMTP by default. Request port 25 access or use an email relay",[60,1208,1209,1212,1213,1215],{},[28,1210,1211],{},"MTA_API_KEY mismatch"," — the key in Docker ",[64,1214,969],{}," must match the one set in Convex env vars",[41,1217,1219],{"id":1218},"clamav-is-slow-to-start","ClamAV is slow to start",[10,1221,1222,1223,1226],{},"This is normal. ClamAV loads virus definitions into memory on startup, which takes 1-2 minutes. The Docker healthcheck has a ",[64,1224,1225],{},"start_period: 120s"," to account for this.",[10,1228,1229,1230,1233],{},"The MTA waits for ClamAV to be healthy before starting (configured via ",[64,1231,1232],{},"depends_on"," in Docker Compose).",[41,1235,1237],{"id":1236},"web-ui-shows-connection-error","Web UI shows connection error",[10,1239,1240,1241,1244,1245,1248],{},"The browser needs to reach the Convex backend directly. If ",[64,1242,1243],{},"NUXT_PUBLIC_CONVEX_URL"," uses a Docker-internal hostname (like ",[64,1246,1247],{},"http:\u002F\u002Fconvex:3210","), the browser can't connect.",[10,1250,1251,1252,1254],{},"Fix: set ",[64,1253,1243],{}," to a URL reachable from the browser:",[925,1256,1257,1263],{},[60,1258,1259,1260],{},"Local dev: ",[64,1261,1262],{},"http:\u002F\u002Flocalhost:3210",[60,1264,1265,1266,1269],{},"Production: ",[64,1267,1268],{},"https:\u002F\u002Fconvex.example.com"," (via reverse proxy)",[41,1271,1273],{"id":1272},"function-deployment-fails","Function deployment fails",[96,1275,1277],{"className":98,"code":1276,"language":100,"meta":101,"style":101},"docker compose --profile deploy run --rm convex-deploy 2>&1\n",[64,1278,1279],{"__ignoreMap":101},[105,1280,1281,1283,1285,1287,1289,1291,1293,1295],{"class":107,"line":108},[105,1282,200],{"class":111},[105,1284,203],{"class":115},[105,1286,251],{"class":130},[105,1288,254],{"class":115},[105,1290,257],{"class":115},[105,1292,260],{"class":130},[105,1294,835],{"class":115},[105,1296,701],{"class":628},[10,1298,1130],{},[925,1300,1301,1310,1319],{},[60,1302,1303,1306,1307],{},[28,1304,1305],{},"CONVEX_ADMIN_KEY is wrong or missing"," — regenerate: ",[64,1308,1309],{},"docker compose exec convex .\u002Fgenerate_admin_key.sh",[60,1311,1312,1315,1316],{},[28,1313,1314],{},"Convex container not healthy"," — check with ",[64,1317,1318],{},"docker compose ps",[60,1320,1321,1324],{},[28,1322,1323],{},"Schema conflict"," — if you modified Convex schema files, check for validation errors in the deploy output",[20,1326,1328],{"id":1327},"migrating-to-production","Migrating to Production",[10,1330,1331],{},"To move from a local development setup to a production deployment:",[57,1333,1334,1377,1387,1397,1410,1521,1530],{},[60,1335,1336,1339,1340,1342,1343],{},[28,1337,1338],{},"Update URLs"," in ",[64,1341,969],{},":",[96,1344,1346],{"className":98,"code":1345,"language":100,"meta":101,"style":101},"NUXT_PUBLIC_CONVEX_URL=https:\u002F\u002Fconvex.example.com\nNUXT_PUBLIC_CONVEX_SITE_URL=https:\u002F\u002Fconvex-site.example.com\nNUXT_PUBLIC_SITE_URL=https:\u002F\u002Fowlat.example.com\n",[64,1347,1348,1357,1367],{"__ignoreMap":101},[105,1349,1350,1352,1354],{"class":107,"line":108},[105,1351,1243],{"class":801},[105,1353,987],{"class":628},[105,1355,1356],{"class":115},"https:\u002F\u002Fconvex.example.com\n",[105,1358,1359,1362,1364],{"class":107,"line":123},[105,1360,1361],{"class":801},"NUXT_PUBLIC_CONVEX_SITE_URL",[105,1363,987],{"class":628},[105,1365,1366],{"class":115},"https:\u002F\u002Fconvex-site.example.com\n",[105,1368,1369,1372,1374],{"class":107,"line":175},[105,1370,1371],{"class":801},"NUXT_PUBLIC_SITE_URL",[105,1373,987],{"class":628},[105,1375,1376],{"class":115},"https:\u002F\u002Fowlat.example.com\n",[60,1378,1379,1382,1383,18],{},[28,1380,1381],{},"Set up DNS"," — configure A records, PTR, SPF, DKIM, and DMARC. See ",[14,1384,1386],{"href":1385},"\u002Fdeveloper\u002Fself-hosting-dns-email","DNS & Email Setup",[60,1388,1389,1392,1393,18],{},[28,1390,1391],{},"Add a reverse proxy"," — Caddy or Nginx with TLS. See ",[14,1394,1396],{"href":1395},"\u002Fdeveloper\u002Fself-hosting-production","Production Deployment",[60,1398,1399,1402,1403,1406,1407,18],{},[28,1400,1401],{},"Secure Redis"," — add ",[64,1404,1405],{},"REDIS_PASSWORD"," as described in ",[14,1408,1396],{"href":1409},"\u002Fdeveloper\u002Fself-hosting-production#redis-authentication",[60,1411,1412,1415,1416],{},[28,1413,1414],{},"Update Convex env vars"," to match the new URLs:",[96,1417,1419],{"className":98,"code":1418,"language":100,"meta":101,"style":101},"npx convex env set SITE_URL \"https:\u002F\u002Fowlat.example.com\" --url http:\u002F\u002Flocalhost:3210 --admin-key \u003Ckey>\nnpx convex env set CONVEX_SITE_URL \"https:\u002F\u002Fconvex-site.example.com\" --url http:\u002F\u002Flocalhost:3210 --admin-key \u003Ckey>\nnpx convex env set ALLOWED_ORIGINS \"https:\u002F\u002Fowlat.example.com\" --url http:\u002F\u002Flocalhost:3210 --admin-key \u003Ckey>\n",[64,1420,1421,1462,1492],{"__ignoreMap":101},[105,1422,1423,1426,1429,1432,1435,1438,1441,1444,1447,1450,1453,1456,1459],{"class":107,"line":108},[105,1424,1425],{"class":111},"npx",[105,1427,1428],{"class":115}," convex",[105,1430,1431],{"class":115}," env",[105,1433,1434],{"class":115}," set",[105,1436,1437],{"class":115}," SITE_URL",[105,1439,1440],{"class":115}," \"https:\u002F\u002Fowlat.example.com\"",[105,1442,1443],{"class":130}," --url",[105,1445,1446],{"class":115}," http:\u002F\u002Flocalhost:3210",[105,1448,1449],{"class":130}," --admin-key",[105,1451,1452],{"class":628}," \u003C",[105,1454,1455],{"class":115},"ke",[105,1457,1458],{"class":801},"y",[105,1460,1461],{"class":628},">\n",[105,1463,1464,1466,1468,1470,1472,1475,1478,1480,1482,1484,1486,1488,1490],{"class":107,"line":123},[105,1465,1425],{"class":111},[105,1467,1428],{"class":115},[105,1469,1431],{"class":115},[105,1471,1434],{"class":115},[105,1473,1474],{"class":115}," CONVEX_SITE_URL",[105,1476,1477],{"class":115}," \"https:\u002F\u002Fconvex-site.example.com\"",[105,1479,1443],{"class":130},[105,1481,1446],{"class":115},[105,1483,1449],{"class":130},[105,1485,1452],{"class":628},[105,1487,1455],{"class":115},[105,1489,1458],{"class":801},[105,1491,1461],{"class":628},[105,1493,1494,1496,1498,1500,1502,1505,1507,1509,1511,1513,1515,1517,1519],{"class":107,"line":175},[105,1495,1425],{"class":111},[105,1497,1428],{"class":115},[105,1499,1431],{"class":115},[105,1501,1434],{"class":115},[105,1503,1504],{"class":115}," ALLOWED_ORIGINS",[105,1506,1440],{"class":115},[105,1508,1443],{"class":130},[105,1510,1446],{"class":115},[105,1512,1449],{"class":130},[105,1514,1452],{"class":628},[105,1516,1455],{"class":115},[105,1518,1458],{"class":801},[105,1520,1461],{"class":628},[60,1522,1523,1526,1527],{},[28,1524,1525],{},"Restart"," the stack: ",[64,1528,1529],{},"docker compose up -d",[60,1531,1532,1535,1536,18],{},[28,1533,1534],{},"Verify"," — send a test email and check delivery with ",[14,1537,1540],{"href":1538,"rel":1539},"https:\u002F\u002Fwww.mail-tester.com",[757],"mail-tester.com",[1542,1543,1544],"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 .sDN9O, html code.shiki .sDN9O{--shiki-default:#6A737D;--shiki-dark:#768390}html pre.shiki code .sviXB, html code.shiki .sviXB{--shiki-default:#005CC5;--shiki-dark:#6CB6FF}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 .s74oq, html code.shiki .s74oq{--shiki-default:#005CC5;--shiki-dark:#F47067}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}",{"title":101,"searchDepth":123,"depth":123,"links":1546},[1547,1553,1562,1563,1564,1569,1576],{"id":22,"depth":123,"text":23,"children":1548},[1549,1550,1552],{"id":43,"depth":175,"text":44},{"id":82,"depth":175,"text":1551},"Option B: owlat upgrade CLI",{"id":143,"depth":175,"text":144},{"id":308,"depth":123,"text":309,"children":1554},[1555,1556,1557,1558,1559,1560,1561],{"id":319,"depth":175,"text":320},{"id":411,"depth":175,"text":412},{"id":538,"depth":175,"text":539},{"id":599,"depth":175,"text":600},{"id":644,"depth":175,"text":645},{"id":677,"depth":175,"text":678},{"id":760,"depth":175,"text":761},{"id":861,"depth":123,"text":862},{"id":919,"depth":123,"text":920},{"id":954,"depth":123,"text":955,"children":1565},[1566,1567,1568],{"id":958,"depth":175,"text":959},{"id":999,"depth":175,"text":1000},{"id":1071,"depth":175,"text":1072},{"id":1107,"depth":123,"text":1108,"children":1570},[1571,1572,1573,1574,1575],{"id":1111,"depth":175,"text":1112},{"id":1162,"depth":175,"text":1163},{"id":1218,"depth":175,"text":1219},{"id":1236,"depth":175,"text":1237},{"id":1272,"depth":175,"text":1273},{"id":1327,"depth":123,"text":1328},"Keep your self-hosted Owlat instance up to date, manage backups, scale performance, and troubleshoot common issues.","md",{},"\u002Fdeveloper\u002Fself-hosting-maintenance",{"title":5,"description":1577},"3.developer\u002F34.self-hosting-maintenance","a1US5puNQR96JwhJbFL5sjQ69wiIeX-MPQg8K1C7OuQ",[1585,1587],{"title":1396,"path":1395,"stem":1586,"children":-1},"3.developer\u002F33.self-hosting-production",{"title":1072,"path":1588,"stem":1589,"children":-1},"\u002Fdeveloper\u002Fconvex","3.developer\u002F4.convex",1777110578829]