Self-Hosted 3D Virtual Tabletop

Your table, on your own server

TerrainTale is not only a hosted product — it is a small, ordinary Node app you can run yourself: one process, one database, one deploy. No dependency on the hosted site's accounts or plans.

The welcome screen with five starter scenes

One process to run it

`npm run build` (bakes SITE_URL into canonical URLs and the sitemap) then `npm start` runs `node server/index.mjs`, which serves the built app, the API and the sign-in gate together. Put it behind any reverse proxy that passes WebSocket upgrades through (Caddy and nginx both do) for TLS.

Data stays where you put it

Everything lives in Postgres — a real `DATABASE_URL` in production, or PGlite (Postgres in-process) under `data/pg/` with none set, so a fresh checkout needs no external database. Uploaded models go to a blob store, Cloudflare R2 or a plain directory (`data/blobs/`). Back up with `pg_dump` plus the bucket or directory.

Sign-in without a hosted account

Email + password, one-time email links, and Google / Facebook / Apple / GitHub / Discord sign-in are all handled by the same server, with no third-party SDK. `ALLOW_SIGNUP=0` or `ALLOWED_EMAILS=…` turns a public copy into a private table for just your group.

No billing unless you turn it on

With no Creem key, no mock and `PLANS_ENFORCED` unset, billing is off entirely: every account is unlimited, the paywall dialog never appears, and the account page reports 200 scenes free — the self-hoster's default. The whole product, including your own model imports, is open on every plan on a self-hosted copy.

Questions

Is the code open source?

Yes, TerrainTale is self-hostable under the repository's licence — clone it, build it, and run it yourself.

Do I need a separate database server?

No. Without `DATABASE_URL` it runs on PGlite, Postgres in-process, under `data/pg/`; set `DATABASE_URL` to point at a real Postgres for production.

Are Premium features gated on a self-hosted copy?

Only if you turn billing on. By default there is no billing at all, so importing your own models and keeping many saved scenes are open on every account.

Does shared-room play (WebSockets) work behind my proxy?

Yes, as long as the reverse proxy passes WebSocket upgrades through — Caddy and nginx do this by default.

Ready to put a map on the table?

Sign up in a minute, no card needed. Five starter scenes are waiting on the welcome screen.