[{"data":1,"prerenderedAt":501},["ShallowReactive",2],{"doc-\u002Fdocs\u002Fself-hosting\u002Fdocker-compose":3,"github-stars":498},{"id":4,"title":5,"body":6,"description":490,"extension":491,"meta":492,"navigation":239,"order":78,"path":493,"section":494,"seo":495,"stem":496,"__hash__":497},"docs\u002Fdocs\u002Fself-hosting\u002Fdocker-compose.md","Docker Compose",{"type":7,"value":8,"toc":480},"minimark",[9,13,22,27,47,51,101,115,119,167,174,178,189,258,285,289,320,331,335,338,367,371,378,406,417,421,452,461,476],[10,11,5],"h1",{"id":12},"docker-compose",[14,15,16,17,21],"p",{},"The repository ships a ",[18,19,20],"code",{},"docker-compose.yml"," that brings up everything an\ninstance needs: a Postgres database and the app, wired together. It pulls the\npublished image, so there is nothing to build, and the app applies its own\ndatabase migrations on startup.",[23,24,26],"h2",{"id":25},"prerequisites","Prerequisites",[28,29,30,41,44],"ul",{},[31,32,33,34,37,38,40],"li",{},"Docker 24+ with the Compose plugin (",[18,35,36],{},"docker compose",", not the legacy\n",[18,39,12],{},")",[31,42,43],{},"Git",[31,45,46],{},"A host with 1 vCPU and 1 GB RAM is enough to start",[23,48,50],{"id":49},"one-command","One command",[52,53,58],"pre",{"className":54,"code":55,"language":56,"meta":57,"style":57},"language-bash shiki shiki-themes github-dark","git clone https:\u002F\u002Fgithub.com\u002FPunterDigital\u002FClerq.git\ncd Clerq\ndocker compose up -d\n","bash","",[18,59,60,76,86],{"__ignoreMap":57},[61,62,65,69,73],"span",{"class":63,"line":64},"line",1,[61,66,68],{"class":67},"svObZ","git",[61,70,72],{"class":71},"sU2Wk"," clone",[61,74,75],{"class":71}," https:\u002F\u002Fgithub.com\u002FPunterDigital\u002FClerq.git\n",[61,77,79,83],{"class":63,"line":78},2,[61,80,82],{"class":81},"sDLfK","cd",[61,84,85],{"class":71}," Clerq\n",[61,87,89,92,95,98],{"class":63,"line":88},3,[61,90,91],{"class":67},"docker",[61,93,94],{"class":71}," compose",[61,96,97],{"class":71}," up",[61,99,100],{"class":81}," -d\n",[14,102,103,104,107,108,114],{},"That command pulls ",[18,105,106],{},"ghcr.io\u002Fpunterdigital\u002Fclerq",", starts Postgres, waits for\nit to become healthy, then starts the app - which migrates the database and\nserves on ",[109,110,111],"a",{"href":111,"rel":112},"http:\u002F\u002Flocalhost:3000",[113],"nofollow",". Open it, create your\naccount, set up your business, and you are in.",[23,116,118],{"id":117},"what-is-in-the-compose-file","What is in the compose file",[120,121,122,135],"table",{},[123,124,125],"thead",{},[126,127,128,132],"tr",{},[129,130,131],"th",{},"Service",[129,133,134],{},"Role",[136,137,138,157],"tbody",{},[126,139,140,146],{},[141,142,143],"td",{},[18,144,145],{},"postgres",[141,147,148,149,152,153,156],{},"The database. Data persists in the ",[18,150,151],{},"postgres-data"," named volume. Its port is bound to ",[18,154,155],{},"127.0.0.1"," only, so it is never exposed to the network.",[126,158,159,164],{},[141,160,161],{},[18,162,163],{},"app",[141,165,166],{},"The Clerq image. It runs any pending migrations on startup, then serves on port 3000.",[14,168,169,170,173],{},"Configure it with a ",[18,171,172],{},".env"," file next to the compose file rather than editing\nthe YAML.",[23,175,177],{"id":176},"configure-with-a-env-file","Configure with a .env file",[14,179,180,181,183,184,188],{},"Compose reads a ",[18,182,172],{}," file in the project root. The defaults work for a local\ntrial, but you ",[185,186,187],"strong",{},"must"," set a real database password and auth secret before\nexposing the instance anywhere:",[52,190,192],{"className":54,"code":191,"language":56,"meta":57,"style":57},"# .env - next to docker-compose.yml\nPOSTGRES_PASSWORD=a-strong-random-password\nBETTER_AUTH_SECRET=generate-with-openssl-rand-base64-32\nBETTER_AUTH_URL=https:\u002F\u002Fclerq.example.com\n\n# Optional: pin a released version instead of latest\nCLERQ_TAG=0.1.0\n",[18,193,194,200,213,223,234,241,247],{"__ignoreMap":57},[61,195,196],{"class":63,"line":64},[61,197,199],{"class":198},"sAwPA","# .env - next to docker-compose.yml\n",[61,201,202,206,210],{"class":63,"line":78},[61,203,205],{"class":204},"s95oV","POSTGRES_PASSWORD",[61,207,209],{"class":208},"snl16","=",[61,211,212],{"class":71},"a-strong-random-password\n",[61,214,215,218,220],{"class":63,"line":88},[61,216,217],{"class":204},"BETTER_AUTH_SECRET",[61,219,209],{"class":208},[61,221,222],{"class":71},"generate-with-openssl-rand-base64-32\n",[61,224,226,229,231],{"class":63,"line":225},4,[61,227,228],{"class":204},"BETTER_AUTH_URL",[61,230,209],{"class":208},[61,232,233],{"class":71},"https:\u002F\u002Fclerq.example.com\n",[61,235,237],{"class":63,"line":236},5,[61,238,240],{"emptyLinePlaceholder":239},true,"\n",[61,242,244],{"class":63,"line":243},6,[61,245,246],{"class":198},"# Optional: pin a released version instead of latest\n",[61,248,250,253,255],{"class":63,"line":249},7,[61,251,252],{"class":204},"CLERQ_TAG",[61,254,209],{"class":208},[61,256,257],{"class":71},"0.1.0\n",[14,259,260,261,264,265,268,269,271,272,274,275,279,280,284],{},"The compose file assembles ",[18,262,263],{},"DATABASE_URL"," for you from the ",[18,266,267],{},"POSTGRES_*"," values\nand points it at the internal ",[18,270,145],{}," service - you do not set\n",[18,273,263],{}," yourself here. See\n",[109,276,278],{"href":277},"\u002Fdocs\u002Fself-hosting\u002Fenvironment-variables","Environment variables"," for the full\nreference, and ",[109,281,283],{"href":282},"\u002Fdocs\u002Fself-hosting\u002Fproduction","Going to production"," to put it\nbehind HTTPS safely.",[23,286,288],{"id":287},"updating","Updating",[52,290,292],{"className":54,"code":291,"language":56,"meta":57,"style":57},"docker compose pull      # fetch the newer image\ndocker compose up -d     # recreate the app; migrations run on boot\n",[18,293,294,306],{"__ignoreMap":57},[61,295,296,298,300,303],{"class":63,"line":64},[61,297,91],{"class":67},[61,299,94],{"class":71},[61,301,302],{"class":71}," pull",[61,304,305],{"class":198},"      # fetch the newer image\n",[61,307,308,310,312,314,317],{"class":63,"line":78},[61,309,91],{"class":67},[61,311,94],{"class":71},[61,313,97],{"class":71},[61,315,316],{"class":81}," -d",[61,318,319],{"class":198},"     # recreate the app; migrations run on boot\n",[14,321,322,323,325,326,330],{},"Pin ",[18,324,252],{}," in production so updates are deliberate. See\n",[109,327,329],{"href":328},"\u002Fdocs\u002Fself-hosting\u002Fbackups-and-upgrades","Backups & upgrades",".",[23,332,334],{"id":333},"build-from-source-instead","Build from source instead",[14,336,337],{},"To run a local build of the code rather than the published image - for\ncontributors, or to try local changes - layer on the build override:",[52,339,341],{"className":54,"code":340,"language":56,"meta":57,"style":57},"docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build\n",[18,342,343],{"__ignoreMap":57},[61,344,345,347,349,352,355,357,360,362,364],{"class":63,"line":64},[61,346,91],{"class":67},[61,348,94],{"class":71},[61,350,351],{"class":81}," -f",[61,353,354],{"class":71}," docker-compose.yml",[61,356,351],{"class":81},[61,358,359],{"class":71}," docker-compose.build.yml",[61,361,97],{"class":71},[61,363,316],{"class":81},[61,365,366],{"class":81}," --build\n",[23,368,370],{"id":369},"seed-demo-data-optional","Seed demo data (optional)",[14,372,373,374,377],{},"To explore a populated instance, seed demo data from a checkout against the\nrunning database (the compose Postgres is published on ",[18,375,376],{},"127.0.0.1:5432","):",[52,379,381],{"className":54,"code":380,"language":56,"meta":57,"style":57},"pnpm install\nDATABASE_URL=postgresql:\u002F\u002Fclerq:clerq@localhost:5432\u002Fclerq pnpm db:seed\n",[18,382,383,391],{"__ignoreMap":57},[61,384,385,388],{"class":63,"line":64},[61,386,387],{"class":67},"pnpm",[61,389,390],{"class":71}," install\n",[61,392,393,395,397,400,403],{"class":63,"line":78},[61,394,263],{"class":204},[61,396,209],{"class":208},[61,398,399],{"class":71},"postgresql:\u002F\u002Fclerq:clerq@localhost:5432\u002Fclerq",[61,401,402],{"class":67}," pnpm",[61,404,405],{"class":71}," db:seed\n",[14,407,408,409,412,413,416],{},"It creates a sample business with clients, projects, a week of tracked time,\nand a login of ",[18,410,411],{},"demo@clerq.local"," \u002F ",[18,414,415],{},"clerq-demo",". The seed is idempotent.\nDo not seed a production instance.",[23,418,420],{"id":419},"stopping-and-resetting","Stopping and resetting",[52,422,424],{"className":54,"code":423,"language":56,"meta":57,"style":57},"docker compose down            # stop the stack, keep the data\ndocker compose down -v         # stop and DELETE the database volume\n",[18,425,426,438],{"__ignoreMap":57},[61,427,428,430,432,435],{"class":63,"line":64},[61,429,91],{"class":67},[61,431,94],{"class":71},[61,433,434],{"class":71}," down",[61,436,437],{"class":198},"            # stop the stack, keep the data\n",[61,439,440,442,444,446,449],{"class":63,"line":78},[61,441,91],{"class":67},[61,443,94],{"class":71},[61,445,434],{"class":71},[61,447,448],{"class":81}," -v",[61,450,451],{"class":198},"         # stop and DELETE the database volume\n",[14,453,454,457,458,460],{},[18,455,456],{},"down -v"," removes the ",[18,459,151],{}," volume and everything in it. Only use it\nwhen you genuinely want a clean slate.",[462,463,464],"blockquote",{},[14,465,466,467,471,472,475],{},"Want the database managed elsewhere, or a single container? See\n",[109,468,470],{"href":469},"\u002Fdocs\u002Fself-hosting\u002Fdocker","Docker"," for the bare ",[18,473,474],{},"docker run"," against your\nown Postgres.",[477,478,479],"style",{},"html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}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 pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}",{"title":57,"searchDepth":88,"depth":88,"links":481},[482,483,484,485,486,487,488,489],{"id":25,"depth":78,"text":26},{"id":49,"depth":78,"text":50},{"id":117,"depth":78,"text":118},{"id":176,"depth":78,"text":177},{"id":287,"depth":78,"text":288},{"id":333,"depth":78,"text":334},{"id":369,"depth":78,"text":370},{"id":419,"depth":78,"text":420},"The one-command self-host. Compose brings up Postgres and the Clerq app together, pulling the published image - no build required.","md",{},"\u002Fdocs\u002Fself-hosting\u002Fdocker-compose","Self-hosting",{"title":5,"description":490},"docs\u002Fself-hosting\u002Fdocker-compose","ZVSshgUzKovUn5RXFSdvW4qMx19SF5V8D_nDg8humBc",{"stars":499,"repo":500},0,"PunterDigital\u002Fclerq",1781535397768]