Self-hosting a virtual tabletop: what it really takes
Self-hosting is sold as either trivial or terrifying, and it is neither. For a virtual tabletop it is roughly the same job as hosting a small website, with one twist: the asset library is large and mostly static, which is the easy kind of large.
· 7 min read · TerrainTale

The box you need is smaller than you think
A table serves a handful of people for a few hours a week. Two shared vCPUs and 4 GB of memory is comfortable — around five euros a month at most European providers. The constraint is disk, because a realistic model library runs to a couple of hundred megabytes, and bandwidth on the first visit while a player caches it.
The pieces
A reverse proxy that terminates TLS and renews certificates on its own. A process serving the built site and the app. Somewhere to keep accounts and sessions. Backups of that one file or database — and nothing else, if scenes live in the player's browser.
Build somewhere else and copy the result up. A production box should never need a toolchain, and a build that runs on a 4 GB VPS is a build that occasionally fails at midnight.
- TLS with automatic renewal — not a yearly calendar reminder.
- One process, restarted on failure.
- Long cache lifetimes on hashed assets, none on HTML.
- A nightly copy of the accounts store, off the box.
The things people forget
Email. Verification and password resets need a sender, and a home IP will not deliver — use a transactional provider. Redirects: pick one hostname and permanently redirect the rest, including the www version and every plain-HTTP request. And a health check, so you learn the site is down before your players do.
When it is worth it
Self-host if you want the data to stay yours, if you enjoy the maintenance, or if you want to keep running the thing after the company that made it changes. Do not self-host to save money on a hosted free tier — the money is not the cost, the Sunday afternoons are.
Serving a large asset library cheaply
The library is the only part of a virtual tabletop that is genuinely heavy, and it is also the easiest part to serve well, because it never changes. Give every hashed asset a one-year immutable cache lifetime and no revalidation; give HTML no cache at all. After that, a player downloads a model once, ever, and your bandwidth bill is dominated by first visits.
If those first visits hurt, a CDN in front costs nothing at this scale and turns the box into a thing that serves only HTML and a small API. Compress text on the way out, and leave the binaries alone — models and HDRIs are already compressed and gzipping them just burns CPU.
- Hashed assets: max-age=31536000, immutable.
- HTML: no-cache, so a deploy is visible immediately.
- Compress text only; binaries are already compressed.
- A CDN in front is optional and cheap insurance.
The redirects and headers a self-hosted site still owes
Even a private table benefits from getting this right once. Pick one hostname and permanently redirect everything else to it in a single hop — the www version, plain http, trailing slashes, /index.html. Use 301 rather than 302 or 308 so the redirect is unambiguous to anything that caches it. Add HSTS once you are confident in the certificate, set nosniff and a frame policy, and you are done for years.
Then check it the boring way: curl each variant and read the status line. Two hops where there should be one is the most common finding, and it is always a five-minute fix.