[{"data":1,"prerenderedAt":1055},["ShallowReactive",2],{"\u002Fblog\u002Fself-host-oxynote":3,"github-stars":1054},{"id":4,"title":5,"author":6,"body":9,"date":1033,"description":1034,"extension":1035,"image":1036,"imageAlt":1037,"imageDark":1038,"meta":1039,"navigation":307,"path":1040,"robots":1041,"seo":1042,"sitemap":1043,"stem":1052,"updated":1041,"__hash__":1053},"blog_en\u002Fblog\u002Fself-host-oxynote.md","How to Self-Host Oxynote with Docker",{"name":7,"url":8},"Dovydas Bykovas","https:\u002F\u002Fgithub.com\u002Fdavseby",{"type":10,"value":11,"toc":1015},"minimark",[12,16,19,24,34,38,114,117,155,158,161,191,202,207,214,325,340,344,359,417,423,455,462,470,484,488,501,510,513,521,525,528,531,547,553,561,564,568,571,586,589,595,598,606,609,617,627,631,641,648,656,672,675,679,689,697,700,727,738,742,746,752,827,834,877,881,884,959,968,979,983,991,1011],[13,14,15],"p",{},"Oxynote is a workspace where pages hold live charts next to the text that explains them. It runs as one container. Under the hood it's several services, its own PostgreSQL database among them, packaged together so you only have one thing to run.",[13,17,18],{},"So setting it up takes a single command. This guide covers the install, the first steps of using the app and how to keep it running: logging in, changing the defaults, writing a page with a live chart, reviewing a change, connecting an AI agent, and backing up and upgrading.",[20,21,23],"h2",{"id":22},"what-you-need","What You Need",[25,26,27,31],"ul",{},[28,29,30],"li",{},"Docker, on an amd64 or arm64 machine. The image is published for both, which covers any recent Mac, Windows PC or Linux server.",[28,32,33],{},"Port 8080 free on that machine, or another free port (see below).",[20,35,37],{"id":36},"start-oxynote","Start Oxynote",[39,40,45],"pre",{"className":41,"code":42,"language":43,"meta":44,"style":44},"language-bash shiki shiki-themes github-light github-dark-default","docker run -d --name oxynote \\\n  -p 8080:8080 \\\n  -v oxynote_data:\u002Foxynote\u002Fdata \\\n  --stop-timeout 60 \\\n  ghcr.io\u002Foxynote\u002Foxynote:latest\n","bash","",[46,47,48,75,86,97,108],"code",{"__ignoreMap":44},[49,50,53,57,61,65,68,71],"span",{"class":51,"line":52},"line",1,[49,54,56],{"class":55},"sYRkA","docker",[49,58,60],{"class":59},"sNbzA"," run",[49,62,64],{"class":63},"sPgVT"," -d",[49,66,67],{"class":63}," --name",[49,69,70],{"class":59}," oxynote",[49,72,74],{"class":73},"sw7DY"," \\\n",[49,76,78,81,84],{"class":51,"line":77},2,[49,79,80],{"class":63},"  -p",[49,82,83],{"class":59}," 8080:8080",[49,85,74],{"class":73},[49,87,89,92,95],{"class":51,"line":88},3,[49,90,91],{"class":63},"  -v",[49,93,94],{"class":59}," oxynote_data:\u002Foxynote\u002Fdata",[49,96,74],{"class":73},[49,98,100,103,106],{"class":51,"line":99},4,[49,101,102],{"class":63},"  --stop-timeout",[49,104,105],{"class":63}," 60",[49,107,74],{"class":73},[49,109,111],{"class":51,"line":110},5,[49,112,113],{"class":59},"  ghcr.io\u002Foxynote\u002Foxynote:latest\n",[13,115,116],{},"What each flag does:",[25,118,119,133,139,149],{},[28,120,121,124,125,128,129,132],{},[46,122,123],{},"-d"," runs the container in the background, and ",[46,126,127],{},"--name"," lets later commands refer to it as ",[46,130,131],{},"oxynote",".",[28,134,135,138],{},[46,136,137],{},"-p 8080:8080"," makes the container's port 8080 reachable on your machine's port 8080.",[28,140,141,144,145,148],{},[46,142,143],{},"-v oxynote_data:\u002Foxynote\u002Fdata"," keeps Oxynote's data in a Docker volume named ",[46,146,147],{},"oxynote_data",", outside the container.",[28,150,151,154],{},[46,152,153],{},"--stop-timeout 60"," gives Oxynote up to a minute to save open documents when it stops. Docker's default of 10 seconds can cut that short.",[13,156,157],{},"The volume holds everything Oxynote stores: pages, uploaded images, settings and the keys it generates on the first start. You can remove the container and start a new one on the same volume without losing anything, which is how upgrades work. Deleting the volume deletes all of it.",[13,159,160],{},"Wait for Oxynote to say it's up:",[39,162,164],{"className":41,"code":163,"language":43,"meta":44,"style":44},"docker logs -f oxynote 2>&1 | grep \"oxynote is up\"\n",[46,165,166],{"__ignoreMap":44},[49,167,168,170,173,176,178,182,185,188],{"class":51,"line":52},[49,169,56],{"class":55},[49,171,172],{"class":59}," logs",[49,174,175],{"class":63}," -f",[49,177,70],{"class":59},[49,179,181],{"class":180},"sDgYj"," 2>&1",[49,183,184],{"class":180}," |",[49,186,187],{"class":55}," grep",[49,189,190],{"class":59}," \"oxynote is up\"\n",[13,192,193,194,197,198,201],{},"Nothing prints until it's ready, because ",[46,195,196],{},"grep"," hides every other line. That usually takes a few seconds. When a line with ",[46,199,200],{},"oxynote is up at http:\u002F\u002Flocalhost:8080"," appears, press Ctrl+C to stop following the logs. Oxynote keeps running.",[203,204,206],"h3",{"id":205},"with-docker-compose","With Docker Compose",[13,208,209,210,213],{},"To keep the setup in a file instead, save this as ",[46,211,212],{},"docker-compose.yaml",":",[39,215,219],{"className":216,"code":217,"filename":212,"language":218,"meta":44,"style":44},"language-yaml shiki shiki-themes github-light github-dark-default","services:\n  oxynote:\n    image: ghcr.io\u002Foxynote\u002Foxynote:latest\n    ports:\n      - \"8080:8080\"\n    volumes:\n      - oxynote_data:\u002Foxynote\u002Fdata\n    stop_grace_period: 60s\n    restart: unless-stopped\n\nvolumes:\n  oxynote_data:\n","yaml",[46,220,221,231,238,249,256,264,272,280,291,302,309,317],{"__ignoreMap":44},[49,222,223,227],{"class":51,"line":52},[49,224,226],{"class":225},"sXQ1o","services",[49,228,230],{"class":229},"szmH1",":\n",[49,232,233,236],{"class":51,"line":77},[49,234,235],{"class":225},"  oxynote",[49,237,230],{"class":229},[49,239,240,243,246],{"class":51,"line":88},[49,241,242],{"class":225},"    image",[49,244,245],{"class":229},": ",[49,247,248],{"class":59},"ghcr.io\u002Foxynote\u002Foxynote:latest\n",[49,250,251,254],{"class":51,"line":99},[49,252,253],{"class":225},"    ports",[49,255,230],{"class":229},[49,257,258,261],{"class":51,"line":110},[49,259,260],{"class":229},"      - ",[49,262,263],{"class":59},"\"8080:8080\"\n",[49,265,267,270],{"class":51,"line":266},6,[49,268,269],{"class":225},"    volumes",[49,271,230],{"class":229},[49,273,275,277],{"class":51,"line":274},7,[49,276,260],{"class":229},[49,278,279],{"class":59},"oxynote_data:\u002Foxynote\u002Fdata\n",[49,281,283,286,288],{"class":51,"line":282},8,[49,284,285],{"class":225},"    stop_grace_period",[49,287,245],{"class":229},[49,289,290],{"class":59},"60s\n",[49,292,294,297,299],{"class":51,"line":293},9,[49,295,296],{"class":225},"    restart",[49,298,245],{"class":229},[49,300,301],{"class":59},"unless-stopped\n",[49,303,305],{"class":51,"line":304},10,[49,306,308],{"emptyLinePlaceholder":307},true,"\n",[49,310,312,315],{"class":51,"line":311},11,[49,313,314],{"class":225},"volumes",[49,316,230],{"class":229},[49,318,320,323],{"class":51,"line":319},12,[49,321,322],{"class":225},"  oxynote_data",[49,324,230],{"class":229},[13,326,327,328,331,332,335,336,339],{},"Then run ",[46,329,330],{},"docker compose up -d"," in the same folder. ",[46,333,334],{},"stop_grace_period"," does the same job as ",[46,337,338],{},"--stop-timeout"," above, so keep it.",[203,341,343],{"id":342},"on-another-port-or-domain","On Another Port or Domain",[13,345,346,347,350,351,354,355,358],{},"Oxynote assumes it is reached at ",[46,348,349],{},"http:\u002F\u002Flocalhost:8080",". To use another port, change the number before the colon in ",[46,352,353],{},"-p",", which is the port on your machine, and set ",[46,356,357],{},"OXYNOTE_PUBLIC_URL"," to the address you open in the browser:",[39,360,362],{"className":41,"code":361,"language":43,"meta":44,"style":44},"docker run -d --name oxynote \\\n  -p 8090:8080 \\\n  -e OXYNOTE_PUBLIC_URL=http:\u002F\u002Flocalhost:8090 \\\n  -v oxynote_data:\u002Foxynote\u002Fdata \\\n  --stop-timeout 60 \\\n  ghcr.io\u002Foxynote\u002Foxynote:latest\n",[46,363,364,378,387,397,405,413],{"__ignoreMap":44},[49,365,366,368,370,372,374,376],{"class":51,"line":52},[49,367,56],{"class":55},[49,369,60],{"class":59},[49,371,64],{"class":63},[49,373,67],{"class":63},[49,375,70],{"class":59},[49,377,74],{"class":73},[49,379,380,382,385],{"class":51,"line":77},[49,381,80],{"class":63},[49,383,384],{"class":59}," 8090:8080",[49,386,74],{"class":73},[49,388,389,392,395],{"class":51,"line":88},[49,390,391],{"class":63},"  -e",[49,393,394],{"class":59}," OXYNOTE_PUBLIC_URL=http:\u002F\u002Flocalhost:8090",[49,396,74],{"class":73},[49,398,399,401,403],{"class":51,"line":99},[49,400,91],{"class":63},[49,402,94],{"class":59},[49,404,74],{"class":73},[49,406,407,409,411],{"class":51,"line":110},[49,408,102],{"class":63},[49,410,105],{"class":63},[49,412,74],{"class":73},[49,414,415],{"class":51,"line":266},[49,416,113],{"class":59},[13,418,419,420,422],{},"With Compose, change these lines in the ",[46,421,131],{}," service:",[39,424,426],{"className":216,"code":425,"filename":212,"language":218,"meta":44,"style":44},"    ports:\n      - \"8090:8080\"\n    environment:\n      - OXYNOTE_PUBLIC_URL=http:\u002F\u002Flocalhost:8090\n",[46,427,428,434,441,448],{"__ignoreMap":44},[49,429,430,432],{"class":51,"line":52},[49,431,253],{"class":225},[49,433,230],{"class":229},[49,435,436,438],{"class":51,"line":77},[49,437,260],{"class":229},[49,439,440],{"class":59},"\"8090:8080\"\n",[49,442,443,446],{"class":51,"line":88},[49,444,445],{"class":225},"    environment",[49,447,230],{"class":229},[49,449,450,452],{"class":51,"line":99},[49,451,260],{"class":229},[49,453,454],{"class":59},"OXYNOTE_PUBLIC_URL=http:\u002F\u002Flocalhost:8090\n",[13,456,457,458,461],{},"Oxynote itself serves plain HTTP. For a real domain, such as ",[46,459,460],{},"notes.example.com",", put a reverse proxy in front of it: a web server that holds the HTTPS certificate for your domain and forwards each request to the container. Caddy is the quickest to set up, because it gets the certificate on its own. With Caddy installed on the same machine and the domain pointing at that machine, this is its whole config:",[39,463,468],{"className":464,"code":466,"language":467},[465],"language-text","notes.example.com {\n    reverse_proxy localhost:8080\n}\n","text",[46,469,466],{"__ignoreMap":44},[13,471,472,473,476,477,480,481,132],{},"Then set ",[46,474,475],{},"OXYNOTE_PUBLIC_URL=https:\u002F\u002Fnotes.example.com",". nginx and Traefik work as well. Connecting an AI agent, covered below, only works on an ",[46,478,479],{},"https:\u002F\u002F"," address or on ",[46,482,483],{},"localhost",[20,485,487],{"id":486},"log-in","Log In",[13,489,490,491,493,494,497,498,132],{},"Open ",[46,492,349],{},". Until you change the password, the login page shows the default account: ",[46,495,496],{},"admin@example.com"," with the password ",[46,499,500],{},"oxynote-admin-1234",[13,502,503],{},[504,505],"img",{"alt":506,"src":507,"src-dark":508,"title":509},"The Oxynote login page with the default credentials","\u002Fimages\u002Fblog\u002Finstall-login.webp","\u002Fimages\u002Fblog\u002Finstall-login-dark.webp","The login page shows the default account until you change its password",[13,511,512],{},"The first start creates one workspace and names it after a place in space. It opens on a welcome page with a getting-started checklist and charts from a built-in demo data source, so there is something live on screen before you connect anything.",[13,514,515],{},[504,516],{"alt":517,"src":518,"src-dark":519,"title":520},"The welcome page of a new Oxynote workspace","\u002Fimages\u002Fblog\u002Finstall-welcome.webp","\u002Fimages\u002Fblog\u002Finstall-welcome-dark.webp","A new workspace, with demo charts already running",[203,522,524],{"id":523},"change-the-defaults","Change the Defaults",[13,526,527],{},"Anyone who can open the login page can sign in with the default account, so change it before other people can reach the instance.",[13,529,530],{},"Oxynote can't send email until you configure an email server (SMTP). Everything works without one, with three differences:",[25,532,533,536,539],{},[28,534,535],{},"There is no password reset. A forgotten password can't be recovered, so store the new one somewhere safe.",[28,537,538],{},"Changing your email asks for your password instead of sending a confirmation link.",[28,540,541,542,546],{},"Invitations aren't emailed. Each pending invitation has a ",[543,544,545],"strong",{},"Copy invitation link"," button instead, and you send the link yourself.",[13,548,490,549,552],{},[543,550,551],{},"Settings"," from the workspace menu at the top of the sidebar:",[13,554,555],{},[504,556],{"alt":557,"src":558,"src-dark":559,"title":560},"Oxynote's settings with the email, username and password","\u002Fimages\u002Fblog\u002Finstall-settings.webp","\u002Fimages\u002Fblog\u002Finstall-settings-dark.webp","Change the email, username and password under Profile",[13,562,563],{},"Change the email and the password, then scroll down and rename the workspace. Once the password changes, the login page stops showing the defaults.",[20,565,567],{"id":566},"write-a-page-with-a-live-chart","Write a Page with a Live Chart",[13,569,570],{},"The screenshots from here on come from a demo workspace named Port.",[13,572,573,574,577,578,581,582,585],{},"Create a page from the sidebar and write a sentence about what it's for. Then, on a new line, type ",[46,575,576],{},"\u002F"," and choose ",[543,579,580],{},"Live Metrics",", or type ",[46,583,584],{},"%%"," as a shortcut.",[13,587,588],{},"Pick the demo Prometheus data source. It serves synthetic data, so a chart works before you connect a real one. Paste this query, a synthetic metric for how long pull requests wait for their first review, and give the chart a title:",[39,590,593],{"className":591,"code":592,"language":467},[465],"engineering_pr_time_to_first_review_minutes\n",[46,594,592],{"__ignoreMap":44},[13,596,597],{},"You can also set the unit, the time range, how often it refreshes and thresholds. A threshold draws a line across the chart. Here it marks a four-hour target for the first review.",[13,599,600],{},[504,601],{"alt":602,"src":603,"src-dark":604,"title":605},"The Live Metrics settings with a Prometheus query and a threshold","\u002Fimages\u002Fblog\u002Finstall-chart-editor.webp","\u002Fimages\u002Fblog\u002Finstall-chart-editor-dark.webp","The query, the unit and a threshold at four hours",[13,607,608],{},"The chart runs its query when someone opens the page and again at its refresh interval. Because it sits inside the text, the sentences around it can say what it shows and what to do when the line crosses the target.",[13,610,611],{},[504,612],{"alt":613,"src":614,"src-dark":615,"title":616},"A page with a live chart between two paragraphs","\u002Fimages\u002Fblog\u002Finstall-page.webp","\u002Fimages\u002Fblog\u002Finstall-page-dark.webp","The target, the chart and what to do when the line crosses it",[13,618,619,620,622,623,626],{},"To connect your own data, open ",[543,621,551],{}," and go to ",[543,624,625],{},"External Data Sources",", where you can choose from Prometheus, PostgreSQL, MySQL and MariaDB.",[20,628,630],{"id":629},"review-changes-like-a-pull-request","Review Changes Like a Pull Request",[13,632,633,634,637,638,132],{},"For pages where a wrong edit matters, such as a runbook or a team's targets, Oxynote has a review flow. The main version becomes read-only, and every edit goes into a draft that a teammate approves before it's merged. To turn it on for a page, open its ",[543,635,636],{},"⋯"," menu and choose ",[543,639,640],{},"Make Reviewable",[13,642,643,644,647],{},"In the draft below, the target drops from four hours to three, and the query now averages the wait per hour. With ",[543,645,646],{},"Show Changes"," on, the draft highlights each edit in the text, and the marker on the chart lists what changed in its settings, here the query and the threshold:",[13,649,650],{},[504,651],{"alt":652,"src":653,"src-dark":654,"title":655},"A draft with a changed sentence and a changed chart query","\u002Fimages\u002Fblog\u002Finstall-review.webp","\u002Fimages\u002Fblog\u002Finstall-review-dark.webp","The draft shows the changed text and the changed chart query",[13,657,658,659,662,663,665,666,669,670,132],{},"To get the draft approved, add a reviewer under ",[543,660,661],{},"Reviewers",". A new install has only your account, so invite a teammate first: open ",[543,664,551],{}," and click ",[543,667,668],{},"Invite"," in the workspace's member list. Without an email server, send them the link from ",[543,671,545],{},[13,673,674],{},"Reviewers can approve the draft or comment on it. Once it's approved, merge it, and the main version shows the new target and the updated chart.",[20,676,678],{"id":677},"connect-an-ai-agent","Connect an AI Agent",[13,680,681,682,684,685,688],{},"Oxynote has a built-in MCP server. MCP (Model Context Protocol) is the standard way AI agents connect to other tools, so an agent such as Claude Code, Cursor or Codex can search, read and edit your pages. To set it up, open ",[543,683,551],{},", go to ",[543,686,687],{},"MCP connections"," and copy the server URL:",[13,690,691],{},[504,692],{"alt":693,"src":694,"src-dark":695,"title":696},"The MCP connections section of Oxynote's settings","\u002Fimages\u002Fblog\u002Finstall-mcp.webp","\u002Fimages\u002Fblog\u002Finstall-mcp-dark.webp","The server URL and the clients you've authorized",[13,698,699],{},"For Claude Code, that's one command. Use the URL you copied, which differs from this one if you changed the port or domain:",[39,701,703],{"className":41,"code":702,"language":43,"meta":44,"style":44},"claude mcp add --transport http oxynote http:\u002F\u002Flocalhost:8080\u002Fcore\u002Fapi\u002Fmcp\n",[46,704,705],{"__ignoreMap":44},[49,706,707,710,713,716,719,722,724],{"class":51,"line":52},[49,708,709],{"class":55},"claude",[49,711,712],{"class":59}," mcp",[49,714,715],{"class":59}," add",[49,717,718],{"class":63}," --transport",[49,720,721],{"class":59}," http",[49,723,70],{"class":59},[49,725,726],{"class":59}," http:\u002F\u002Flocalhost:8080\u002Fcore\u002Fapi\u002Fmcp\n",[13,728,729,730,733,734,737],{},"In Claude Code, run ",[46,731,732],{},"\u002Fmcp"," and authorize Oxynote in the browser window that opens. The agent then shows up under ",[543,735,736],{},"Authorized clients",". On reviewable pages, agents edit the draft like everyone else, so their changes go through review too.",[20,739,741],{"id":740},"back-up-and-upgrade","Back Up and Upgrade",[203,743,745],{"id":744},"back-up","Back Up",[13,747,748,749,751],{},"Everything lives in the ",[46,750,147],{}," volume, so a full backup is a copy of it. Stop Oxynote first, so nothing changes during the copy:",[39,753,755],{"className":41,"code":754,"language":43,"meta":44,"style":44},"docker stop oxynote\ndocker run --rm -v oxynote_data:\u002Fdata -v \"$PWD\":\u002Fbackup alpine \\\n  tar czf \u002Fbackup\u002Foxynote-data.tar.gz -C \u002Fdata .\ndocker start oxynote\n",[46,756,757,767,798,818],{"__ignoreMap":44},[49,758,759,761,764],{"class":51,"line":52},[49,760,56],{"class":55},[49,762,763],{"class":59}," stop",[49,765,766],{"class":59}," oxynote\n",[49,768,769,771,773,776,779,782,784,787,790,793,796],{"class":51,"line":77},[49,770,56],{"class":55},[49,772,60],{"class":59},[49,774,775],{"class":63}," --rm",[49,777,778],{"class":63}," -v",[49,780,781],{"class":59}," oxynote_data:\u002Fdata",[49,783,778],{"class":63},[49,785,786],{"class":59}," \"",[49,788,789],{"class":229},"$PWD",[49,791,792],{"class":59},"\":\u002Fbackup",[49,794,795],{"class":59}," alpine",[49,797,74],{"class":73},[49,799,800,803,806,809,812,815],{"class":51,"line":88},[49,801,802],{"class":59},"  tar",[49,804,805],{"class":59}," czf",[49,807,808],{"class":59}," \u002Fbackup\u002Foxynote-data.tar.gz",[49,810,811],{"class":63}," -C",[49,813,814],{"class":59}," \u002Fdata",[49,816,817],{"class":59}," .\n",[49,819,820,822,825],{"class":51,"line":99},[49,821,56],{"class":55},[49,823,824],{"class":59}," start",[49,826,766],{"class":59},[13,828,829,830,833],{},"This writes ",[46,831,832],{},"oxynote-data.tar.gz"," to your current folder. To restore it, for example on a new machine, unpack it into an empty volume, then start Oxynote with the command from the top of this guide:",[39,835,837],{"className":41,"code":836,"language":43,"meta":44,"style":44},"docker run --rm -v oxynote_data:\u002Fdata -v \"$PWD\":\u002Fbackup alpine \\\n  tar xzf \u002Fbackup\u002Foxynote-data.tar.gz -C \u002Fdata\n",[46,838,839,863],{"__ignoreMap":44},[49,840,841,843,845,847,849,851,853,855,857,859,861],{"class":51,"line":52},[49,842,56],{"class":55},[49,844,60],{"class":59},[49,846,775],{"class":63},[49,848,778],{"class":63},[49,850,781],{"class":59},[49,852,778],{"class":63},[49,854,786],{"class":59},[49,856,789],{"class":229},[49,858,792],{"class":59},[49,860,795],{"class":59},[49,862,74],{"class":73},[49,864,865,867,870,872,874],{"class":51,"line":77},[49,866,802],{"class":59},[49,868,869],{"class":59}," xzf",[49,871,808],{"class":59},[49,873,811],{"class":63},[49,875,876],{"class":59}," \u002Fdata\n",[203,878,880],{"id":879},"upgrade","Upgrade",[13,882,883],{},"Pull the new image, stop and remove the old container, then start a new one on the same volume. Oxynote updates its database when it starts, so there is no separate migration step.",[39,885,887],{"className":41,"code":886,"language":43,"meta":44,"style":44},"docker pull ghcr.io\u002Foxynote\u002Foxynote:latest\ndocker stop oxynote && docker rm oxynote\ndocker run -d --name oxynote \\\n  -p 8080:8080 \\\n  -v oxynote_data:\u002Foxynote\u002Fdata \\\n  --stop-timeout 60 \\\n  ghcr.io\u002Foxynote\u002Foxynote:latest\n",[46,888,889,899,917,931,939,947,955],{"__ignoreMap":44},[49,890,891,893,896],{"class":51,"line":52},[49,892,56],{"class":55},[49,894,895],{"class":59}," pull",[49,897,898],{"class":59}," ghcr.io\u002Foxynote\u002Foxynote:latest\n",[49,900,901,903,905,907,910,912,915],{"class":51,"line":77},[49,902,56],{"class":55},[49,904,763],{"class":59},[49,906,70],{"class":59},[49,908,909],{"class":229}," && ",[49,911,56],{"class":55},[49,913,914],{"class":59}," rm",[49,916,766],{"class":59},[49,918,919,921,923,925,927,929],{"class":51,"line":88},[49,920,56],{"class":55},[49,922,60],{"class":59},[49,924,64],{"class":63},[49,926,67],{"class":63},[49,928,70],{"class":59},[49,930,74],{"class":73},[49,932,933,935,937],{"class":51,"line":99},[49,934,80],{"class":63},[49,936,83],{"class":59},[49,938,74],{"class":73},[49,940,941,943,945],{"class":51,"line":110},[49,942,91],{"class":63},[49,944,94],{"class":59},[49,946,74],{"class":73},[49,948,949,951,953],{"class":51,"line":266},[49,950,102],{"class":63},[49,952,105],{"class":63},[49,954,74],{"class":73},[49,956,957],{"class":51,"line":274},[49,958,113],{"class":59},[13,960,961,962,965,966,132],{},"With Compose, run ",[46,963,964],{},"docker compose pull",", then ",[46,967,330],{},[13,969,970,971,978],{},"The ",[972,973,977],"a",{"href":974,"rel":975},"https:\u002F\u002Fgithub.com\u002Foxynote\u002Foxynote\u002Fblob\u002Fmain\u002Fdocker\u002Fprod\u002FREADME.md",[976],"nofollow","operator README"," covers the rest: an external PostgreSQL, email, object storage, social login, the AI assistant and the workspace limits.",[20,980,982],{"id":981},"wrapping-up","Wrapping Up",[13,984,985,986,990],{},"Next, connect your own data. ",[972,987,989],{"href":988},"\u002Fblog\u002Fconnect-postgresql","The PostgreSQL guide"," walks through it.",[13,992,993,994,999,1000,1005,1006,132],{},"Oxynote is open source: ",[972,995,998],{"href":996,"rel":997},"https:\u002F\u002Fgithub.com\u002Foxynote\u002Foxynote",[976],"github.com\u002Foxynote\u002Foxynote",". If a step here doesn't work on your machine, we'd like to hear about it: ask on ",[972,1001,1004],{"href":1002,"rel":1003},"https:\u002F\u002Fdiscord.gg\u002FmNJd5zJkvC",[976],"Discord"," or ",[972,1007,1010],{"href":1008,"rel":1009},"https:\u002F\u002Fgithub.com\u002Foxynote\u002Foxynote\u002Fissues",[976],"open an issue",[1012,1013,1014],"style",{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sw7DY, html code.shiki .sw7DY{--shiki-default:#005CC5;--shiki-dark:#FF7B72}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 .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}",{"title":44,"searchDepth":77,"depth":77,"links":1016},[1017,1018,1022,1025,1026,1027,1028,1032],{"id":22,"depth":77,"text":23},{"id":36,"depth":77,"text":37,"children":1019},[1020,1021],{"id":205,"depth":88,"text":206},{"id":342,"depth":88,"text":343},{"id":486,"depth":77,"text":487,"children":1023},[1024],{"id":523,"depth":88,"text":524},{"id":566,"depth":77,"text":567},{"id":629,"depth":77,"text":630},{"id":677,"depth":77,"text":678},{"id":740,"depth":77,"text":741,"children":1029},[1030,1031],{"id":744,"depth":88,"text":745},{"id":879,"depth":88,"text":880},{"id":981,"depth":77,"text":982},"2026-10-28","Oxynote runs as one container, so installing it takes a single docker run. This guide covers the install and the first steps in the app.","md","\u002Fimages\u002Fblog\u002Finstall-cover.webp","A page change in Oxynote with a rewritten chart query next to a rewritten sentence","\u002Fimages\u002Fblog\u002Finstall-cover-dark.webp",{},"\u002Fblog\u002Fself-host-oxynote",null,{"title":5,"description":1034},{"loc":1040,"images":1044,"lastmod":1033},[1045,1046,1047,1048,1049,1050,1051],{"loc":507},{"loc":518},{"loc":558},{"loc":603},{"loc":614},{"loc":653},{"loc":694},"blog\u002Fself-host-oxynote","WUME39N6LhSwrNcRFBjoQP_4Xemki2VEnXm0E6QpwVo",464,1793619147702]