Self-hosted onboarding tool that provisions Open Mercato dev environments
in Coder workspaces β one click per signup.
A signup tool that provisions a self-hosted Coder workspace running an Open Mercato dev environment per signup. A user signs up at the onboarding UI, clicks Create sandbox, and lands in a fresh workspace with browser-based VS Code, a web terminal, the live build splash, and direct port forwards to the running Mercato app. Every workspace ships with the opencode, codex, and claude code CLIs preinstalled, so AI-assisted edits work out of the box.
βββββββββββββββββββββββ docker compose (host) βββββββββββββββββββββββ
β β
β postgres-onboarding postgres-coder coder-server β
β :5545 :5432 (internal) :80 internal β
β β² β² β² β
β β β β β
β next-onboarding (3000) βββββββΌβββββββββββββββ β
β β admin API token β
β β β
β nginx edge: :80 redirect-only, :443 HTTPS/WSS β
β sandbox.lvh.me β onboarding, coder.sandbox.lvh.me β Coder, β
β *.apps.sandbox.lvh.me β Coder wildcard app proxy β
β β
β bind-mount: /var/run/docker.sock βββββββββββββββββββββββββββββββΊ β
β (coder-server uses host Docker daemon to spawn workspaces)β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
Workspace container (one per signup, mercato-workspace:latest):
- node 24, yarn 4, code-server, postgres-client, git
- opencode + codex + claude code CLIs on PATH
- sidecar postgres (pgvector pg17) on a private docker network
- on first start: npx create-mercato-app@develop, yarn setup
- splash on :4000, app on :3000, code-server on :13337
- 16 GB RAM, 4 vCPU
| Service | Image | Purpose |
|---|---|---|
postgres-onboarding |
postgres:17-alpine |
Onboarding app's user + sandbox tables |
postgres-coder |
postgres:17 |
Coder control-plane metadata DB |
coder |
ghcr.io/coder/coder:latest |
Coder server on internal :80; spawns workspaces via host Docker |
onboarding |
node:24-alpine (custom build) |
Next.js 15 signup + dashboard UI on :3000 |
edge |
nginx:alpine |
Standard-port HTTPS/WSS edge for onboarding, Coder, and wildcard apps |
- π₯οΈ Browser-based VS Code (code-server)
- π» Web terminal
- βοΈ Live build splash on port 4000
- π The Mercato app on its own subdomain (
<workspace>.lvh.melocally,<workspace>.<your-domain>in prod) β full asset URLs, WebSockets, no path-prefix surprises - π€ opencode, codex, and claude code CLIs preinstalled
- π Sidecar PostgreSQL 17 (pgvector)
- πͺ 16 GB RAM allocation per workspace
cp .env.example .env # set OPENAI_API_KEY (and optionally ANTHROPIC_API_KEY)
./start.sh # ~3 min on first run, ~30 s afterwards
open https://sandbox.lvh.me # sign up and create your sandboxFirst run takes ~3 min β Coder has to boot, the workspace image has to build, and the template has to be pushed. Subsequent
./start.shruns are idempotent and finish in ~30 s.
- Onboarding (your users) β
https://sandbox.lvh.meβ any email + 8+ character password. - Coder admin (operators) β
https://coder.sandbox.lvh.meβadmin@local.dev/Sup3rSecret!.
- Sign up at
https://sandbox.lvh.me. - Land on the dashboard.
- Click Create sandbox, give it a name, submit.
- Wait ~30β60 s for status to flip to ready.
- Click into VS Code, Terminal, Splash, or App.
The stack is published by one nginx edge proxy on standard ports. HTTP on
:80 redirects to HTTPS on :443; all interactive traffic uses HTTPS/WSS.
- Onboarding:
https://sandbox.lvh.me - Coder:
https://coder.sandbox.lvh.me - App:
https://3000--main--<workspace>--<user>.apps.sandbox.lvh.me - Splash:
https://4000--main--<workspace>--<user>.apps.sandbox.lvh.me - VS Code:
https://13337--main--<workspace>--<user>.apps.sandbox.lvh.me/?folder=/home/coder/app - Terminal:
https://coder.sandbox.lvh.me/@<user>/<workspace>/terminal
Local dev uses SANDBOX_DOMAIN=sandbox.lvh.me, which wildcards every
subdomain to 127.0.0.1; no /etc/hosts edits are required. ./start.sh
generates .runtime/tls/sandbox-lvh-me.crt and
.runtime/tls/sandbox-lvh-me.key if they are missing. Trust the certificate in
your OS/browser keychain to remove privacy warnings.
Fresh workspace agents do not use the browser-facing HTTPS URL to bootstrap.
The template rewrites Coder's generated agent script to the internal Docker
network URL http://coder.${SANDBOX_DOMAIN} by default (AGENT_CODER_URL can
override this). This is required locally because coder.sandbox.lvh.me resolves
to the Coder container on Docker DNS, where :443 is intentionally not open.
For production:
SANDBOX_DOMAIN=sandbox.example.com
WILDCARD_APPS_DOMAIN=apps.sandbox.example.com
CODER_ACCESS_URL=https://coder.sandbox.example.com
CODER_PUBLIC_URL=https://coder.sandbox.example.com
CODER_WILDCARD_ACCESS_URL=*.apps.sandbox.example.com
PROXY_SCHEME=https
COOKIE_SECURE=true
Production TLS must cover ${SANDBOX_DOMAIN}, *.${SANDBOX_DOMAIN}, and
*.apps.${SANDBOX_DOMAIN}.
The Coder agent reports ready as soon as the startup script returns, so the splash and VS Code are usable immediately. The Mercato app on port 3000 takes another 3β7 minutes on first start while yarn install, generate, db migrate, initialize, and the Next.js compile finish β the app URL returns 502 until Next.js prints Ready in Xms. Watch the splash for live progress.
opencode, codex, and claude are on PATH inside every workspace terminal. Set OPENAI_API_KEY and (optionally) ANTHROPIC_API_KEY in .env before running ./start.sh so the template picks them up. If you change the keys later, rerun:
bash scripts/push-template.shWorkspaces created before the keys were set will not have them β recreate those workspaces from the onboarding UI.
Production deploys go to a single Hetzner VPS under *.sandbox.openmercato.com
with a wildcard Let's Encrypt cert (DNS-01 via Cloudflare). The deploy is
idempotent and safe to re-run β every named docker volume (Coder DB,
onboarding DB, issued certs, workspace homes, sidecar postgres data) is
preserved across redeploys.
# On the VPS:
git clone https://github.com/<org>/dokploy-sandboxes-poc.git /opt/mercato-sandboxes
cd /opt/mercato-sandboxes
cp .env.production.example .env.production
$EDITOR .env.production # CLOUDFLARE_API_TOKEN, JWT_SECRET, passwords
./scripts/deploy-hetzner.shSee .ai/SPEC-PROD.md for the canonical production spec β DNS prerequisites, the wildcard-TLS architecture, the data-preservation contract, and the operational runbook.
make ps # show stack containers
make logs # tail compose logs
make stop # docker compose down (preserves volumes)
make reset # docker compose down -v + wipe .runtime/ (irreversible)
make test # run the Playwright happy-path suitemake test runs the Playwright happy-path suite in e2e/. The stack must already be up (./start.sh). Takes ~50 s with warm caches.
- Coder restart-looping β
docker logs coder(usuallyhost.docker.internalresolution). - Workspace stuck on "building" β
docker logs coder-<owner>-<workspace>anddocker logs coder-<workspace_id>-postgres. - Port 5544 in use β we now default to 5545 (orphan postgres detection); override via
ONBOARDING_DB_PORTin.env. - Disk full β
./reset.sh && docker system prune -a --volumes.
.
βββ apps/onboarding/ Next.js 15 signup + dashboard + sandbox UI
βββ coder/
β βββ template/ Terraform: agent + sidecar pgvector + workspace
β βββ workspace-image/ Dockerfile for mercato-workspace:latest
βββ scripts/ bootstrap-coder, build-workspace-image, push-template, migrate-onboarding
βββ e2e/ Playwright happy-path suite
βββ proxy/ nginx edge config template
βββ .ai/ specs, worklist, handoff notes
βββ AGENTS.md coding standards + operational lessons for agents
βββ docker-compose.yml postgres-onboarding, postgres-coder, coder, onboarding, edge
βββ start.sh / stop.sh / reset.sh
βββ Makefile
βββ README.md
- Email delivery (we surface the temp Coder password on screen rather than emailing).
- Multi-tenancy / orgs in Coder (single default org).
- Certificate automation for production TLS. The stack expects cert/key files mounted under
.runtime/tls. - Production hardening (TLS, secret management, OIDC).
This project is licensed under the AGPL-3.0 β the same license as Coder. Open Mercato itself is licensed by its maintainers separately.
- Coder β workspace runtime
- Open Mercato β modular ERP foundation
- Next.js + shadcn/ui β onboarding UI
- Playwright β E2E tests
Built for learning AI-assisted engineering at scale. Powered by Open Mercato.