Repository navigation
Self-hosting the full stack outside Cloudflare — working Miniflare-based deployment, offering fixes upstream #352
Description
Activity
Would be great if there is a blessed self-hosting guide. Ideally all powered by docker-compose. That will exponentially increase adoption. We are also blocked on this part right now in our company.
Hey @tlvenn this is amazing you selfhost it even though it still quite painful to self host right now!
My goal is it to make self hosting Maple as easy as possible best case making it a one click Selfhost experience, would love to see your fixes upstreamed to make it easier for people.Instead of miniflare I was thinking of using https://github.com/denoland/celld long term for a bit more performance miniflare is lacking in, in my experience.
If you have any issues/painponts or similar with self hosting etc. feel free to create an issue regardless of what it is or DM me directly
Thanks — and glad the direction resonates! We took a close look at celld, since a production-grade runtime is exactly what we'd want long-term too. TL;DR: celld looks like the right destination, but Maple's current worker surface can't run on it yet — the gaps are in Maple's binding choices, not in our deployment glue. Here's the api + alerting surface mapped against celld v0.1.0 (per
docs/cloudflare-compat.md, as of today):Maple uses today celld v0.1.0 DO CHAT_SESSION(SQLite)✅ core strength — replicated + hibernating, nicer than anything we get from Miniflare fetch workers, JS RPC, service bindings, vars✅ Hyperdrive → Postgres ( postgres.jsover TCP)❌ not planned, and no TCP sockets ( connect()is an inert stub) — the DB layer can't reach Postgres at allcron triggers / scheduledhandler (4 on api, 4 on alerting)❌ not planned — celld's answer is DO alarms KV ( MCP_SESSIONS)❌ not planned — celld's answer is a DO Queues (vcs-sync, planetscale-webhooks) ❌ "if demand appears"; no queuehandlerWorkflows (clickhouse-schema-apply, ai-triage) ⏳ planned ratelimit binding ❌ unknown wrangler keys fail the deploy broad nodejs_compat⚠️ much narrower than workerd — the api bundle is untested territorySo a celld-native Maple is really an upstream refactor: sessions KV → DO, queues → DO alarms, workflows → celld's planned Workflows, crons → alarms, and — the hardest one — a non-TCP Postgres path in
packages/db(celld does support outbound WebSockets, so a Neon-serverless-style WS driver or a pg-over-WS gateway would work). All very doable, and honestly aligned with celld's "everything on DOs" philosophy — but it's your call on sequencing, and until then the Miniflare container is a working bridge (it's running the full stack for us today: api + alerting + electric-sync, Hyperdrive→Postgres, Electric shapes end-to-end).Fun detail: of our three workers, the only one that runs on celld today is
electric-sync(fetch + vars only) — we may prototype that as a first cell.Meanwhile I'll start upstreaming the low-hanging fixes from the issue (web Dockerfile/.dockerignore first). For the stale api Dockerfile + compose references — want them fixed or removed? Happy to shape the PR either way.
We run Maple fully self-hosted (no Cloudflare, no Tinybird): vanilla ClickHouse per
docs/self-hosted-clickhouse.mdwith the prebuilt collector, plus the three workers (api,alerting,electric-sync) running under Miniflare in a single container, Hyperdrive bound to a local Postgres 17, Electric fronted by the electric-sync worker, drizzle migrations on boot, chat/AI-triage on the OpenRouter fallback. It's validated end-to-end (auth → Electric shapes → ClickHouse queries) and lives at tlvenn/maple (deploy/workerd/).Along the way we found the in-repo Docker path has drifted:
apps/api/Dockerfilerunsbun run startwhich no longer exists,docker-compose.ymlreferencesapps/alerting/apps/electric-syncDockerfiles that aren't in the tree, andapps/web/Dockerfilecan't build because.dockerignoreexcludespackages/browser/packages/effect-sdkand@maple-dev/clickhouse-builder's dist is never built (also@maple-dev/browseris imported by the web app but not declared, so it only resolves via hoisting).If there's interest, I'd like to upstream this in pieces:
No hard feelings if (3) isn't the direction you want — even just (1)+(2) would help other self-hosters.