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.

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.
Keep reading
Save, Export and Import D&D Scenes as JSON
Scenes autosave to your account and are also plain JSON: export a scene to a file, import one back, or hand a recipe to an AI assistant.
Import Your Own 3D Models Into the VTT
Drop your own .glb, .obj, .fbx, .stl and more onto the table: Meshy, Sketchfab and Blender exports, Draco/meshopt files.
Pricing
Free and Premium side by side, and what changes between them.
D&D Virtual Tabletop in 3D
A 3D virtual tabletop built for Dungeons & Dragons: realistic battle maps, animated miniatures, a ruler in feet, initiative and HP.
From the blog
Do you need an account to try TerrainTale?
Signing in gates /app/, but there's nothing to buy to get in. What an account is for, how quickly you can be building a scene, and what happens if you never upgrade.
· 4 min read
Exporting a scene as JSON: what it's for
A TerrainTale scene isn't locked inside the app — File ▸ Export gives you the whole thing as plain JSON. What that's actually useful for, and how it differs from autosave and from sharing a live room.
· 5 min read
Self-hosting vs. the hosted plan: which one's for you
TerrainTale is open source and self-hostable, and a self-hosted copy has no billing gate at all by default. When that's worth the setup, and when the hosted plan is simply less work.
· 6 min read
All articles on battle maps, grid rules and running the game
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.