Under the hood · D&D 2024 (5.5e)

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

The welcome screen with five starter scenes
Real screenshot from the app.

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.

The welcome screen with five starter scenes

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.

Questions

Is TerrainTale open source?

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

Is Premium's saved-scene limit or own-file-import gate active on a self-hosted copy?

Not by default — with no billing key, no mock and PLANS_ENFORCED unset, billing is off entirely and every account is unlimited (200 scenes, own-file imports open).

Do I need a separate database server to self-host?

No, without DATABASE_URL it runs on PGlite (Postgres in-process) under data/pg/; set DATABASE_URL to a real Postgres for production.

What does the hosted plan cost if I'd rather not self-host?

Free keeps 1 saved scene; Premium is $6/mo or $48/yr for up to 200 and your own model imports.

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.