Guide · tested October 14, 2026

HTML5 multiplayer game server hosting

Your HTML, JavaScript and textures can live on a static host. Your real-time multiplayer server also needs a runtime that holds connections open and keeps the game's state in memory. This guide puts both behind one domain on a Linux VPS.

Test scope: with Node.js 26 and Caddy 2.10.2 on Linux, we fetched the page through Caddy and connected two WebSocket clients. Both received player IDs, a shared two-player state and ping replies. The proxy check used local HTTP; public DNS, certificate issuance, systemd service startup and a paid VPS were not tested.

What you are deploying

Browser: https://game.example.com + wss://game.example.com
    → Caddy on ports 80/443
    → Node.js game server on port 3000

The browser-game tutorial supplies the Node.js server and HTML page. Caddy serves as the reverse proxy for both normal page requests and WebSocket upgrades. The page chooses ws:// on local HTTP and wss:// on HTTPS using location.protocol.

Keeping the page and socket on one domain avoids an extra endpoint setting in this example. If you later move the page to a CDN, change the socket URL to your game-server domain and validate allowed origins on the server.

1. Prove it works locally

Download the example ZIP and unzip it. Install Node.js 22 or later and Caddy from its official instructions. Our run used the versions in the test note above.

cd browser-multiplayer
npm install --omit=dev
node server.js
# Open http://localhost:3000 in two tabs.

Stop that process, then run the downloadable proxy smoke test:

python3 deploy/smoke-test.py /absolute/path/to/caddy
# PASS HTTP page through Caddy
# PASS two WebSocket players, shared state and ping replies through Caddy

The test starts Node.js on port 13000 and Caddy on port 18080, swaps only the domain and upstream in the supplied Caddyfile, then stops both processes. It checks application messages, not just an HTTP 101 handshake. Use unused local ports when running it.

2. Choose an always-running runtime

For this unmodified Node.js process, choose a VPS or a managed service that supports persistent WebSocket servers. Hosting that only publishes files cannot run it. Request-oriented serverless runtimes may require a different architecture; they are not a drop-in home for an in-memory game loop. For example, Cloudflare Durable Objects support WebSockets, but require adapting the server to that runtime.

A DigitalOcean basic VPS is listed at $4/month with 512 MiB RAM and 500 GiB transfer, or $6/month with 1 GiB and 1,000 GiB transfer, checked October 14, 2026. Domain registration, backups, tax and transfer overages can add costs. These figures do not establish how many players your game can support.

Colyseus Cloud starts at $15/month if you build with its framework. Playroom is a different SDK-based approach. See the comparison and linked price sources before committing to a provider.

3. Copy the example to the VPS

Use a current Linux distribution with systemd, install Node.js and Caddy, and copy the example's files to /opt/browser-game. The sample service expects Node at /usr/bin/node; check command -v node and adjust the absolute path if needed.

cd /opt/browser-game
npm install --omit=dev
sudo useradd --system --user-group --no-create-home browsergame
# The account needs read access to server.js, public/ and node_modules/.
sudo install -m 644 deploy/game-server.service /etc/systemd/system/browser-game.service
sudo systemctl daemon-reload
sudo systemctl enable --now browser-game
curl --fail http://127.0.0.1:3000/ -o /dev/null
sudo journalctl -u browser-game -n 50 --no-pager

The systemd unit runs the game without root and restarts after a crash. A restart loses this example's in-memory state and disconnects players. It does not restore matches or reconnect clients automatically.

4. Add the domain and HTTPS proxy

Create an A record for your game hostname pointing to the VPS public IPv4 address. Add an AAAA record only if IPv6 reaches the same server. Replace the sample hostname in the Caddyfile:

game.example.com {
    reverse_proxy 127.0.0.1:3000
}

Install it as /etc/caddy/Caddyfile on a VPS dedicated to this example; if Caddy already serves other sites, add the site block to the existing file instead. Validate and reload:

sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
sudo systemctl reload caddy
sudo journalctl -u caddy -n 50 --no-pager

Allow inbound TCP 80 and 443 in the host and provider firewalls, preserve SSH access, and keep direct inbound port 3000 blocked. With DNS and those ports reachable, Caddy's automatic HTTPS obtains and renews a certificate. Its reverse proxy supports WebSocket upgrades without a separate upgrade-header configuration.

5. Verify from outside the VPS

Open https://YOUR_GAME_DOMAIN in two browser tabs or on two devices. Both should show both players moving. Check the browser's network panel for a connection to wss://YOUR_GAME_DOMAIN with status 101 and incoming state messages.

Run the included bots against that endpoint to inspect round-trip time and payload size:

node test-clients.js wss://YOUR_GAME_DOMAIN 2

This test creates two synthetic players for about ten seconds. Use it on your own endpoint. Remote latency includes the network; local results cannot predict it. The code is a learning example: add input validation, connection/message limits, rooms, authentication where needed, and monitoring before a public release.

Common hosting failures

  • Page loads, socket fails: check that an HTTPS page uses wss://, that Node is alive, and that the proxy points to the correct port.
  • Caddy returns 502: first check curl http://127.0.0.1:3000/ on the VPS and the game-service logs.
  • No certificate: check DNS, stale AAAA records and inbound ports 80/443. Read Caddy's logs.
  • Players lose the match on deploy: this process stores state in memory. Plan draining, reconnects and persistence explicitly.
  • Costs grow with players: broadcasting every player's state to every other player grows traffic roughly with the square of room size. Use small rooms; see the first guide's measurements.

Download the setup

Download the complete browser example, or inspect the Caddyfile, service unit and proxy smoke test.

Your next step

Find a host that fits your setup.

Compare hosting ↗