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 pnpm2. 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 this | Why |
|---|---|
package.json, pnpm-lock.yaml, pnpm-workspace.yaml, .npmrc | Workspace manifests — the install fails without all four |
packages/shared | Game config, constants, protocol, formulas |
packages/db including drizzle/ | Database layer and the migration SQL |
packages/world-gen | World generation |
packages/game-server | The server itself |
packages/plugin-sandbox | Workspace dependency of the server |
apps/client | Only if you're self-hosting the client too (see step 6) |
And leave these behind:
| Skip this | Why |
|---|---|
node_modules/ | That install is Windows-resolved and includes native binaries. It will not run on Linux — you reinstall in step 3 |
apps/admin | The 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.
| Variable | Purpose |
|---|---|
NODE_ENV=production | Run in production mode |
DB_MODE=remote | Use your Postgres (not the embedded PGlite database) |
DATABASE_URL | Your Postgres / Supabase connection string |
GAME_SERVER_PORT | Game server port (default 3001) |
ADMIN_API_KEY | Shared secret that lets your editor push content to this server |
CONFIG_ENCRYPTION_KEY | Encrypts 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 mode | Set these |
|---|---|
| Supabase (your own project) | SUPABASE_URL + SUPABASE_ANON_KEY (optionally SUPABASE_JWT_SECRET) |
| Local accounts | JWT_SECRET — username/password handled entirely by your server, no external service |
| Guests only | Set neither. Anyone can play, nothing is tied to an account |
About the database
WithDB_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 buildThat 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
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-gameThe . 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
| Symptom | Cause |
|---|---|
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 empty | packages/db/drizzle/ didn't make it onto the VPS, so no migrations ran |
Crash on startup mentioning a .node file or an invalid ELF header | You copied Windows node_modules. Delete it and reinstall on the VPS (step 3) |
| Root URL returns 404 | Expected — the game server has no homepage. Check /health or /status |
| Client loads, but never connects to the game | Either 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 in | No auth mode configured — the server fell back to guests-only (step 4) |
| Settings you saved earlier stopped working | CONFIG_ENCRYPTION_KEY changed. Restore the original value |