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. A minimal deploy: clone the repository onto a small server, install dependencies, set SITE_URL to your domain, build, and start the server under a process manager such as systemd so it comes back after a reboot. The 3D rendering happens in each player's browser, not on the server, so a modest machine handles a group. Library models are served with the app, so players need nothing installed.
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. The database holds accounts and saved scenes. The blob store holds uploaded model files and token pictures, which grow quickly if your group imports photoscans, so put that directory on a disk with room. A nightly database dump plus a copy of the blob directory on another machine is a complete backup. Restoring is loading the dump and putting the files back.
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. For a private group, the simplest setup is an invite list with your players' addresses and one-time email links for sign-in. Mail goes out through Resend when its key is set; without it, the messages are printed to the server console, which is enough to test with. Each social sign-in provider needs its own client ID and secret from that provider.
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. If you do want to charge, for example to cover a server for a community, billing runs through Creem and the same limits as the hosted site apply: one saved scene on free, 200 on Premium. Shared rooms, the fog brush, the dice tray and the initiative tracker work the same either way.
Questions
Is the code open source?
Yes, TerrainTale is self-hostable under the repository's licence: clone it, build it, and run it yourself. Read the licence file before redistributing a modified copy or running it as a service for others.
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. PGlite is fine for one group on one machine. Move to a real Postgres when you want managed backups or a database you already run.
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. Turning it on takes a Creem key, the billing mock, or PLANS_ENFORCED set.
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. If players connect but the table never goes live, check the proxy's WebSocket setting first.
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, painted-miniature figures, a ruler in feet, initiative and HP.
From the blog
The best free virtual tabletops in 2026
What each free VTT is actually good at — Roll20, Foundry, Owlbear Rodeo, Alchemy, TerrainTale — and how to pick by what your table does on a Tuesday night rather than by feature count.
· 9 min read
Self-hosting a virtual tabletop: what it really takes
A realistic account of running your own VTT — the box, the bandwidth, TLS, accounts, backups — and when self-hosting is worth it versus using a hosted table.
· 7 min read
Reach weapons in D&D 2024: what 10 feet of reach does on a grid
The reach weapon rules in 5e: which weapons have it, attacking two squares away, opportunity attacks at 10 feet, diagonals, Polearm Master and Sentinel, and how Large creatures with reach cover a map.
· 5 min read
All articles on battle maps, grid rules and running the game
Ready to put a map on the table?
Open the builder and start, no account needed. Sixteen starter scenes are on the welcome screen; sign up free when you want to save one or play it with your group.