Run your game server on a VPS you control, connect it to your database, and share a public play link.

Self-Hosting on a VPS

Run your game server on a Linux VPS you control. This is the fullest-control way to put a game online (prefer one-click? Managed Hosting is available in private beta).

> You may only need to host the game server. If you use the ED5 public play link, we serve the client for you and point it at your server — so the whole client build step below is optional. Read step 6 before you start.

What you need

  • A Linux VPS (Ubuntu 22.04 / Debian 12). Start around 2 vCPU / 4 GB RAM and scale up for more players.
  • Node 20 and pnpm 9 on the VPS.
  • A PostgreSQL database for production — your own Supabase project is fine. Do not use the embedded PGlite database online (see Production Database).
  • A domain with DNS you can point at the VPS.
  • Basic comfort with a Linux terminal. Every step below is copy-paste, but you will be using SSH.

1. Provision the server

Create the VPS, then install Node 20 and pnpm:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
npm i -g pnpm

2. Copy the engine onto the VPS

This is the step people get wrong most often, so it's worth being precise.

Your Studio install already contains a complete engine tree. On Windows it lives at:

%APPDATA%\@ed5-mmo-studio\studio\engine\

Copy that folder itself — the root of it. You want the level that contains package.json, pnpm-lock.yaml, pnpm-workspace.yaml, .npmrc, apps/ and packages/.

> Do not copy just packages/game-server. It is a workspace package, not a standalone application — on its own it will not start. It imports @ed5-mmo-studio/shared, @ed5-mmo-studio/db, @ed5-mmo-studio/world-gen and @ed5-mmo-studio/plugin-sandbox at runtime, and the database migrations live in a different package.

The minimum set for a game-only server:

Copy thisWhy
package.json, pnpm-lock.yaml, pnpm-workspace.yaml, .npmrcWorkspace manifests — the install fails without all four
packages/sharedGame config, constants, protocol, formulas
packages/db including drizzle/Database layer and the migration SQL
packages/world-genWorld generation
packages/game-serverThe server itself
packages/plugin-sandboxWorkspace dependency of the server
apps/clientOnly if you're self-hosting the client too (see step 6)

And leave these behind:

Skip thisWhy
node_modules/That install is Windows-resolved and includes native binaries. It will not run on Linux — you reinstall in step 3
apps/adminThe authoring tools. Never put these on a public server
data/Your local PGlite database and assets — production uses your Postgres

> packages/db/drizzle/ is not optional. The server applies its migrations from that folder on every startup. If the folder is missing, the server still boots — and silently applies nothing. This is the single most common self-hosting failure, and it looks like a mysterious "the database is empty / tables don't exist" problem later.

3. Install dependencies

From the engine root on the VPS:
cd /opt/ed5
pnpm install --prod --filter @ed5-mmo-studio/game-server...

If the lockfile complains, retry with pnpm install --prod --no-frozen-lockfile.

The engine ships with its packages already built, so there is no compile step for the server. The trailing ... matters — it pulls in the workspace dependencies too.

4. Configure environment

Create a .env file at the engine root (the same folder as package.json). The server walks up from its own location to find it.
VariablePurpose
NODE_ENV=productionRun in production mode
DB_MODE=remoteUse your Postgres (not the embedded PGlite database)
DATABASE_URLYour Postgres / Supabase connection string
GAME_SERVER_PORTGame server port (default 3001)
ADMIN_API_KEYShared secret that lets your editor push content to this server
CONFIG_ENCRYPTION_KEYEncrypts sensitive settings stored in the database

> Set CONFIG_ENCRYPTION_KEY once and keep it safe. It decrypts settings already stored in your database — change it later and those settings become unreadable. Back it up somewhere you won't lose it.

Then pick how players sign in — see Auth Setup for the full picture:

Auth modeSet these
Supabase (your own project)SUPABASE_URL + SUPABASE_ANON_KEY (optionally SUPABASE_JWT_SECRET)
Local accountsJWT_SECRET — username/password handled entirely by your server, no external service
Guests onlySet neither. Anyone can play, nothing is tied to an account

About the database

With DB_MODE=remote, a Supabase connection string is automatically routed through Supabase's transaction pooler (port 6543) with prepared statements disabled. That is the supported configuration — paste your normal Supabase connection string and let the server handle it. Don't force the direct 5432 endpoint.

Migrations apply automatically on startup from packages/db/drizzle/. You do not need to run a migration command by hand. Watch the first boot all the way through before assuming it worked — and if you see no migration activity at all, re-read the warning in step 2.

5. Run it

Start the server from the engine root — path resolution depends on it:
cd /opt/ed5
npm i -g pm2
pm2 start "node packages/game-server/dist/index.js" --name game-server
pm2 save && pm2 startup

> The game server has no homepage. Opening its root URL in a browser returns a 404 — that is correct and by design. To check that it's alive, use /health or /status. The /status response includes a bootErrors list, which is the first place to look when something is wrong.

6. Do you need to host the client?

Usually not. You have two options:

Option A — use the ED5 play link (recommended). Register your server in your account dashboard (step 8) and enable the public play link. We serve the client and point it at your server's wss:// address. Nothing more to build, deploy, or keep updated.

Option B — self-host the client too. Only if you want the client on your own domain. Build it before deploying, because the URLs are baked in at build time:

NEXT_PUBLIC_GAME_SERVER_WS_URL=wss://game.yourdomain.com \
  pnpm --filter @ed5-mmo-studio/client build

That produces a standalone bundle under apps/client/.next/standalone/ — copy .next/static and public alongside the generated server.js, then run it on port 3000.

> NEXT_PUBLIC_ values are frozen at build time. Setting them after you build has no effect, and the client will keep trying to reach whatever address it was built with. The client bundled inside your Studio install was built with local defaults — rebuild it with your own URLs. Changing your domain later means rebuilding.

7. HTTPS + WebSocket

Put a reverse proxy (nginx, Caddy, or Cloudflare) in front and terminate TLS:
  • the game server on a subdomain → 127.0.0.1:3001
  • the client, if you're hosting it, on your main domain → 127.0.0.1:3000
> Forward the Upgrade and Connection headers on the game-server route. Without them, everything looks fine — the health check passes, the page loads — and players simply never connect. Caddy does this automatically; nginx needs it configured explicitly.

Use Let's Encrypt or Caddy automatic TLS so the wss:// endpoint is secure, then firewall ports 3000 and 3001 so only the proxy can reach them.

8. Go live

With the server reachable over HTTPS/WSS, open your ED5 account dashboard, register the server under Connect your own VPS (its public URL + wss:// URL), then enable the public play link for your project and share it. See Publishing Your Game.

Prefer Docker?

The engine includes the same container definition our managed servers run. It handles the build, the production install, and shipping the migrations for you. Build it with the engine root as the build context:
cd /opt/ed5
docker build -f packages/game-server/Dockerfile -t ed5-game .
docker run -d --restart=always --env-file .env -p 3001:3001 ed5-game

The . at the end is important — the build needs the whole workspace, not just the server folder.

Keep admin private

The admin app (authoring tools, port 3002) and your database should never be publicly exposed. Author your world in Studio on your own machine and publish to the live server — you don't need admin running on the VPS at all.

Troubleshooting

SymptomCause
Cannot find module '@ed5-mmo-studio/shared'You copied only packages/game-server. Copy the engine root (step 2)
Server starts, but tables are missing or the world is emptypackages/db/drizzle/ didn't make it onto the VPS, so no migrations ran
Crash on startup mentioning a .node file or an invalid ELF headerYou copied Windows node_modules. Delete it and reinstall on the VPS (step 3)
Root URL returns 404Expected — the game server has no homepage. Check /health or /status
Client loads, but never connects to the gameEither the proxy isn't forwarding Upgrade/Connection, or the client was built with the wrong NEXT_PUBLIC_GAME_SERVER_WS_URL
Players connect but can't sign inNo auth mode configured — the server fell back to guests-only (step 4)
Settings you saved earlier stopped workingCONFIG_ENCRYPTION_KEY changed. Restore the original value