Self-hosting vs. the hosted plan: which one's for you
TerrainTale isn't only a hosted product with a Free and Premium plan — it's an ordinary, open-source Node app you can run yourself. Running it yourself changes the billing math completely: with no key and no flag set, a self-hosted copy skips the plan gate entirely.
· 6 min read · TerrainTale

What running it yourself takes
`npm run build` bakes your `SITE_URL` into canonical URLs and the sitemap, then `npm start` runs `node server/index.mjs`, one process that 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 and shared-room play to work. `ALLOW_SIGNUP=0` or `ALLOWED_EMAILS=…` turns a public copy into a private table for just your group.

Data stays wherever you put it
Everything lives in Postgres: point `DATABASE_URL` at a real one for production, or run with none set and get PGlite (Postgres in-process) under `data/pg/`, so a fresh checkout needs no external database at all. Uploaded model files go to a blob store — Cloudflare R2, or just a plain directory (`data/blobs/`). Back it up with `pg_dump` plus the bucket or directory, on whatever schedule you choose.
No billing gate, by default
This is the part worth being precise about: with no Creem API key, no billing mock, and `PLANS_ENFORCED` unset, billing is off entirely on a self-hosted copy. Every account is unlimited — the account page itself reports 200 scenes free — the upgrade dialog never appears, and importing your own model files works on every account without the Premium gate the hosted site applies. That's the self-hoster's default configuration, and it's also what the project's own CI runs against.
The hosted site's `PLANS_ENFORCED` flag exists specifically to simulate the paid gate without a live payment key (so a store reviewer signing up meets the real paid experience) — it's an opt-in flag a self-hoster would have to set deliberately to reproduce Free/Premium at all. Nobody running their own copy for their own table needs to touch it.
- Default self-hosted config: billing off, every account unlimited (200 scenes, own-file imports open)
- The hosted site enforces Free (1 scene) vs. Premium (200, $6/mo or $48/yr) — that's specific to terraintale.com
- PLANS_ENFORCED is the flag that would turn billing on for a self-hoster who deliberately wants it
Sign-in without depending on the hosted site
Email + password, one-time email links, and Google / Facebook / Apple / GitHub / Discord sign-in are all handled by the same self-hosted server with no third-party SDK and no dependency on the hosted site's accounts. A self-hosted copy's users, sessions and saved scenes are entirely separate from anything on terraintale.com.
When each one makes sense
The hosted plan is less work: sign up, and the whole product — including the paid tier's 200 saved scenes and your own model imports for $6/mo or $48/yr — is running with nothing to deploy or maintain. It's the right call for most groups who just want to play.
Self-hosting makes sense when you specifically want your data on infrastructure you control, a private table only your group can reach (`ALLOW_SIGNUP=0`), or simply don't want a saved-scene limit or an own-file-import gate at all and are willing to run one Node process and a database to get that. It's open source under the repository's licence, so there's nothing to buy to try it — only the hosting itself to arrange.